<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#222" media="(prefers-color-scheme: dark)"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/hexo/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/hexo/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/hexo/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/hexo/images/logo.svg" color="#222">

<link rel="stylesheet" href="/hexo/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/blue/pace-theme-bounce.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"codeleader.gitee.io","root":"/hexo/","images":"/hexo/images","scheme":"Gemini","darkmode":true,"version":"8.14.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/hexo/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/hexo/js/config.js"></script>

    <meta name="description" content="Java基础 &#x3D;&#x3D;，equals和hashCode的区别  &#x3D;&#x3D;:对于基本数据类型,&#x3D;&#x3D;比较的是值，对于引用数据类型，&#x3D;&#x3D;比较的是内存地址。 equals:对于没有重写equals方法的类，equals方法和&#x3D;&#x3D;作用类似；对于重写过equals方法的类，equals比较的是值。  hashCode方法返回对象的散列码，返回值是int类型。散列码的作用是确定对象在哈希表中的索引位置。">
<meta property="og:type" content="article">
<meta property="og:title" content="面试知识点总结(持续更新中)">
<meta property="og:url" content="https://codeleader.gitee.io/hexo/2023/08/11/%E9%9D%A2%E8%AF%95%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/index.html">
<meta property="og:site_name" content="CodeLeader">
<meta property="og:description" content="Java基础 &#x3D;&#x3D;，equals和hashCode的区别  &#x3D;&#x3D;:对于基本数据类型,&#x3D;&#x3D;比较的是值，对于引用数据类型，&#x3D;&#x3D;比较的是内存地址。 equals:对于没有重写equals方法的类，equals方法和&#x3D;&#x3D;作用类似；对于重写过equals方法的类，equals比较的是值。  hashCode方法返回对象的散列码，返回值是int类型。散列码的作用是确定对象在哈希表中的索引位置。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230629100728692.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230706111234138.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/a478c74d-2c48-40ae-9374-87aacf05188c.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230706112049155.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722164653609.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722165831540.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/java-collection-hierarchy.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708115413707.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708115434822.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722172359653.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722172408436.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708164303190.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708164340649.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708174345789.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230814163938377.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708174317589.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230814163425044.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722173556712.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722174926942.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722175030601.png">
<meta property="og:image" content="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-4/2019-4%E6%AD%BB%E9%94%811.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230716220638662.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808111612153.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815113635791.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230810165036137.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230811113606308.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230811135708995.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815151620733.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230811100217214.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812114458546.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816141647750.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812150230204.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816142350335.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812123654014.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816142541632.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812155600807.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816143711131.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816144446451.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816145753117.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812175441516.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816150748221.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816150957878.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/mark-and-sweep-garbage-collection-algorithm.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/copying-garbage-collection-algorithm.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815172544300.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816112645827.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/serial-garbage-collector.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/parnew-garbage-collector.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/serial-garbage-collector.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/parallel-scavenge-garbage-collector.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/cms-garbage-collector.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816115724431.png">
<meta property="og:image" content="https://oss.javaguide.cn/github/javaguide/mysql/summary-of-mysql-field-types.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709130937991.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709134109900-16668534893372.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230730181803915.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/mysql20210420165326946.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709074801215.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/1557565-20220429110413866-1755798300.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709080648851.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/mysql20210420165341868.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155155628.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155454797.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155747152.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155910501.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801163556062.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801172557926.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161759827.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161801004.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161802538.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161803933.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802143255849.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802144826618.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802155413278.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802155810213.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802155842162.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802162859281.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802163930423.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802164022191.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204222219378.PNG">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802165134303.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802171749497.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802172421442.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802172736490.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808152038722.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808153322054.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817104857287.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817105249355.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804145343691.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804145930965.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804152407247.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804155120873.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817165247620.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/230ae587a322d6e4d09510161987d346.jpeg">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808164719217.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20210809181452421.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817212919655.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230628161332880.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230805172947843.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/20230806104624.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815230828549.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230702145608644.png">
<meta property="og:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230805160515305.png">
<meta property="article:published_time" content="2023-08-11T14:51:12.148Z">
<meta property="article:modified_time" content="2023-12-16T05:53:51.186Z">
<meta property="article:author" content="别团等shy哥发育">
<meta property="article:tag" content="Java">
<meta property="article:tag" content="面经">
<meta property="article:tag" content="MySQL">
<meta property="article:tag" content="Redis">
<meta property="article:tag" content="RabbitMQ">
<meta property="article:tag" content="JVM">
<meta property="article:tag" content="SpringBoot">
<meta property="article:tag" content="设计模式">
<meta property="article:tag" content="计算机网络">
<meta property="article:tag" content="操作系统">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230629100728692.png">


<link rel="canonical" href="https://codeleader.gitee.io/hexo/2023/08/11/%E9%9D%A2%E8%AF%95%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"https://codeleader.gitee.io/hexo/2023/08/11/%E9%9D%A2%E8%AF%95%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/","path":"2023/08/11/面试核心知识点总结/","title":"面试知识点总结(持续更新中)"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>面试知识点总结(持续更新中) | CodeLeader</title>
  








  <noscript>
    <link rel="stylesheet" href="/hexo/css/noscript.css">
  </noscript>
<style>mjx-container[jax="SVG"] {
  direction: ltr;
}

mjx-container[jax="SVG"] > svg {
  overflow: visible;
}

mjx-container[jax="SVG"][display="true"] {
  display: block;
  text-align: center;
  margin: 1em 0;
}

mjx-container[jax="SVG"][justify="left"] {
  text-align: left;
}

mjx-container[jax="SVG"][justify="right"] {
  text-align: right;
}

g[data-mml-node="merror"] > g {
  fill: red;
  stroke: red;
}

g[data-mml-node="merror"] > rect[data-background] {
  fill: yellow;
  stroke: none;
}

g[data-mml-node="mtable"] > line[data-line] {
  stroke-width: 70px;
  fill: none;
}

g[data-mml-node="mtable"] > rect[data-frame] {
  stroke-width: 70px;
  fill: none;
}

g[data-mml-node="mtable"] > .mjx-dashed {
  stroke-dasharray: 140;
}

g[data-mml-node="mtable"] > .mjx-dotted {
  stroke-linecap: round;
  stroke-dasharray: 0,140;
}

g[data-mml-node="mtable"] > svg {
  overflow: visible;
}

[jax="SVG"] mjx-tool {
  display: inline-block;
  position: relative;
  width: 0;
  height: 0;
}

[jax="SVG"] mjx-tool > mjx-tip {
  position: absolute;
  top: 0;
  left: 0;
}

mjx-tool > mjx-tip {
  display: inline-block;
  padding: .2em;
  border: 1px solid #888;
  font-size: 70%;
  background-color: #F8F8F8;
  color: black;
  box-shadow: 2px 2px 5px #AAAAAA;
}

g[data-mml-node="maction"][data-toggle] {
  cursor: pointer;
}

mjx-status {
  display: block;
  position: fixed;
  left: 1em;
  bottom: 1em;
  min-width: 25%;
  padding: .2em .4em;
  border: 1px solid #888;
  font-size: 90%;
  background-color: #F8F8F8;
  color: black;
}

foreignObject[data-mjx-xml] {
  font-family: initial;
  line-height: normal;
  overflow: visible;
}

.MathJax path {
  stroke-width: 3;
}

mjx-container[display="true"] {
  overflow: auto hidden;
}

mjx-container[display="true"] + br {
  display: none;
}
</style></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/hexo/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">CodeLeader</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">欲买桂花同载酒，终不似少年游</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/hexo/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/hexo/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/hexo/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签<span class="badge">19</span></a></li><li class="menu-item menu-item-categories"><a href="/hexo/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类<span class="badge">4</span></a></li><li class="menu-item menu-item-archives"><a href="/hexo/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档<span class="badge">17</span></a></li><li class="menu-item menu-item-schedule"><a href="/hexo/schedule/" rel="section"><i class="fa fa-calendar fa-fw"></i>日程表</a></li><li class="menu-item menu-item-sitemap"><a href="/hexo/sitemap.xml" rel="section"><i class="fa fa-sitemap fa-fw"></i>站点地图</a></li><li class="menu-item menu-item-commonweal"><a href="/hexo/404/" rel="section"><i class="fa fa-heartbeat fa-fw"></i>公益 404</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#java%E5%9F%BA%E7%A1%80"><span class="nav-number">1.</span> <span class="nav-text">Java基础</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#equals%E5%92%8Chashcode%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">1.1.</span> <span class="nav-text">&#x3D;&#x3D;，equals和hashCode的区别</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#stringstringbufferstringbuilder%E5%8C%BA%E5%88%AB"><span class="nav-number">1.2.</span> <span class="nav-text">String、StringBuffer、StringBuilder区别</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#string%E8%AE%BE%E8%AE%A1%E4%B8%BA%E4%B8%8D%E5%8F%AF%E5%8F%98%E7%9A%84%E5%A5%BD%E5%A4%84%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">1.2.1.</span> <span class="nav-text">String设计为不可变的好处有哪些？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%B8%B8%E9%87%8F%E6%B1%A0"><span class="nav-number">1.3.</span> <span class="nav-text">字符串常量池</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#string%E7%9A%84intern%E6%96%B9%E6%B3%95"><span class="nav-number">1.4.</span> <span class="nav-text">String的intern方法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%B5%85%E6%8B%B7%E8%B4%9D%E4%B8%8E%E6%B7%B1%E6%8B%B7%E8%B4%9D"><span class="nav-number">1.5.</span> <span class="nav-text">浅拷贝与深拷贝</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%A3%85%E7%AE%B1%E4%B8%8E%E6%8B%86%E7%AE%B1"><span class="nav-number">1.6.</span> <span class="nav-text">装箱与拆箱</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%96%B9%E6%B3%95%E9%87%8D%E5%86%99%E5%92%8C%E9%87%8D%E8%BD%BD%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">1.7.</span> <span class="nav-text">方法重写和重载的区别？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java%E7%A8%8B%E5%BA%8F%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%BA%E5%BA%8F"><span class="nav-number">1.8.</span> <span class="nav-text">Java程序初始化顺序</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#io%E6%B5%81"><span class="nav-number">1.9.</span> <span class="nav-text">IO流</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#bionioaio%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">1.10.</span> <span class="nav-text">BIO、NIO、AIO的区别</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96"><span class="nav-number">1.11.</span> <span class="nav-text">Java序列化和反序列化</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6"><span class="nav-number">1.12.</span> <span class="nav-text">反射机制</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java%E5%BC%82%E5%B8%B8%E4%BD%93%E7%B3%BB"><span class="nav-number">1.13.</span> <span class="nav-text">Java异常体系</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%BC%82%E5%B8%B8%E7%9A%84%E5%A4%84%E7%90%86%E6%96%B9%E5%BC%8F"><span class="nav-number">1.13.1.</span> <span class="nav-text">异常的处理方式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java%E6%B3%A8%E8%A7%A3"><span class="nav-number">1.14.</span> <span class="nav-text">Java注解</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java%E6%B3%9B%E5%9E%8B"><span class="nav-number">1.15.</span> <span class="nav-text">Java泛型</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B3%9B%E5%9E%8B%E7%B1%BB%E6%88%96%E6%B3%9B%E5%9E%8B%E6%8E%A5%E5%8F%A3"><span class="nav-number">1.15.1.</span> <span class="nav-text">泛型类或泛型接口</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B3%9B%E5%9E%8B%E4%B8%8A%E9%99%90"><span class="nav-number">1.15.2.</span> <span class="nav-text">泛型上限</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E5%9E%8B%E9%80%9A%E9%85%8D%E7%AC%A6"><span class="nav-number">1.15.3.</span> <span class="nav-text">类型通配符</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B3%9B%E5%9E%8B%E6%93%A6%E9%99%A4"><span class="nav-number">1.15.4.</span> <span class="nav-text">泛型擦除</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#comparable%E5%92%8Ccomparator%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">1.16.</span> <span class="nav-text">Comparable和Comparator的区别(*)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AE%9A%E5%88%B6%E6%AF%94%E8%BE%83%E5%99%A8"><span class="nav-number">1.16.1.</span> <span class="nav-text">(1)定制比较器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AE%9E%E7%8E%B0compareto"><span class="nav-number">1.16.2.</span> <span class="nav-text">(2)实现compareTo()</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#java%E9%9B%86%E5%90%88"><span class="nav-number">2.</span> <span class="nav-text">Java集合</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B8%B8%E7%94%A8%E7%9A%84%E9%9B%86%E5%90%88%E7%B1%BB"><span class="nav-number">2.1.</span> <span class="nav-text">常用的集合类</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#list"><span class="nav-number">2.2.</span> <span class="nav-text">List</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#arraylist%E5%8A%A8%E6%80%81%E6%95%B0%E7%BB%84"><span class="nav-number">2.2.1.</span> <span class="nav-text">ArrayList(动态数组)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#linkedlist%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8"><span class="nav-number">2.2.2.</span> <span class="nav-text">LinkedList(双向链表)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#vector%E5%8A%A8%E6%80%81%E6%95%B0%E7%BB%84%E5%AE%9E%E7%8E%B0%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8"><span class="nav-number">2.2.3.</span> <span class="nav-text">Vector(动态数组实现、线程安全)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#set"><span class="nav-number">2.3.</span> <span class="nav-text">Set</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#hashset%E5%93%88%E5%B8%8C%E8%A1%A8"><span class="nav-number">2.3.1.</span> <span class="nav-text">HashSet(哈希表)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#linkedhashsethashsetlinkedhashmap"><span class="nav-number">2.3.2.</span> <span class="nav-text">LinkedHashSet(HashSet+LinkedHashMap)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#treeset%E7%BA%A2%E9%BB%91%E6%A0%91%E8%87%AA%E5%B9%B3%E8%A1%A1%E7%9A%84%E4%BA%8C%E5%8F%89%E6%8E%92%E5%BA%8F%E6%A0%91"><span class="nav-number">2.3.3.</span> <span class="nav-text">TreeSet(红黑树，自平衡的二叉排序树)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#queue"><span class="nav-number">2.4.</span> <span class="nav-text">Queue</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#queue%E4%B8%8Edeque%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">2.4.1.</span> <span class="nav-text">Queue与Deque的区别</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#priorityqueue%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97"><span class="nav-number">2.4.2.</span> <span class="nav-text">PriorityQueue(优先级队列)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#blockingqueue"><span class="nav-number">2.4.3.</span> <span class="nav-text">BlockingQueue</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#map"><span class="nav-number">2.5.</span> <span class="nav-text">Map</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#hashmap%E6%95%B0%E7%BB%84%E9%93%BE%E8%A1%A8%E7%BA%A2%E9%BB%91%E6%A0%91"><span class="nav-number">2.5.1.</span> <span class="nav-text">HashMap(数组+链表+红黑树)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#hashmap%E4%B8%BA%E4%BB%80%E4%B9%88%E7%BA%BF%E7%A8%8B%E4%B8%8D%E5%AE%89%E5%85%A8"><span class="nav-number">2.5.1.1.</span> <span class="nav-text">HashMap为什么线程不安全？</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#linkedhashmap%E8%AE%B0%E5%BD%95%E6%8F%92%E5%85%A5%E9%A1%BA%E5%BA%8F"><span class="nav-number">2.5.2.</span> <span class="nav-text">LinkedHashMap(记录插入顺序)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#hashtable%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8"><span class="nav-number">2.5.3.</span> <span class="nav-text">HashTable(线程安全)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#treemap%E5%8F%AF%E6%8E%92%E5%BA%8F"><span class="nav-number">2.5.4.</span> <span class="nav-text">TreeMap(可排序)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#concurrenthashmap%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8"><span class="nav-number">2.5.5.</span> <span class="nav-text">ConcurrentHashMap(线程安全)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#listsetmap%E4%B8%89%E8%80%85%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">2.6.</span> <span class="nav-text">List、Set、Map三者的区别？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B8%B8%E7%94%A8%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6%E7%9A%84%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0"><span class="nav-number">2.7.</span> <span class="nav-text">常用集合框架的底层实现</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B"><span class="nav-number">3.</span> <span class="nav-text">Java并发编程</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86"><span class="nav-number">3.1.</span> <span class="nav-text">多线程基础知识</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">3.1.1.</span> <span class="nav-text">进程和线程的区别？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%A0%86%E5%92%8C%E6%96%B9%E6%B3%95%E5%8C%BA"><span class="nav-number">3.1.2.</span> <span class="nav-text">堆和方法区</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B9%B6%E8%A1%8C%E5%92%8C%E5%B9%B6%E5%8F%91%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">3.1.3.</span> <span class="nav-text">并行和并发的区别？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%90%8C%E6%AD%A5%E5%92%8C%E5%BC%82%E6%AD%A5"><span class="nav-number">3.1.4.</span> <span class="nav-text">同步和异步</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E5%92%8C%E7%BA%BF%E7%A8%8B%E4%B8%8D%E5%AE%89%E5%85%A8"><span class="nav-number">3.1.5.</span> <span class="nav-text">线程安全和线程不安全</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%92%8C%E7%8A%B6%E6%80%81"><span class="nav-number">3.1.6.</span> <span class="nav-text">线程的生命周期和状态</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%AD%BB%E9%94%81"><span class="nav-number">3.1.7.</span> <span class="nav-text">死锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#java%E4%B8%AD%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B%E5%92%8C%E7%94%A8%E6%88%B7%E7%BA%BF%E7%A8%8B"><span class="nav-number">3.1.8.</span> <span class="nav-text">Java中守护线程和用户线程</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B8%B8%E8%A7%81%E7%9A%84java%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%88%9B%E5%BB%BA%E6%96%B9%E5%BC%8F%E7%BB%99%E5%87%BA%E4%BB%A3%E7%A0%81%E7%A4%BA%E4%BE%8B"><span class="nav-number">3.2.</span> <span class="nav-text">常见的Java线程的创建方式(给出代码示例)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="nav-number">3.3.</span> <span class="nav-text">线程池</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8%E7%BA%BF%E7%A8%8B%E6%B1%A0%E4%BB%8B%E7%BB%8D%E4%B8%8B%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E5%87%A0%E4%B8%AA%E5%8F%82%E6%95%B0"><span class="nav-number">3.3.1.</span> <span class="nav-text">为什么用线程池？介绍下线程池的几个参数</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%9A%84%E6%96%B9%E6%B3%95"><span class="nav-number">3.3.2.</span> <span class="nav-text">创建线程池的方法</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">3.4.</span> <span class="nav-text">线程通信方式(待补充)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#synchronized%E5%85%B3%E9%94%AE%E5%AD%97"><span class="nav-number">3.5.</span> <span class="nav-text">Synchronized关键字</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#threadlocal%E7%94%A8%E8%BF%87%E5%90%97%E5%AE%83%E6%98%AF%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E7%BB%93%E6%9E%84%E4%BA%86%E8%A7%A3%E5%90%97"><span class="nav-number">3.6.</span> <span class="nav-text">ThreadLocal用过吗？它是怎么解决线程安全的？结构了解吗？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#reentrantlock%E5%8F%AF%E9%87%8D%E5%85%A5%E9%94%81"><span class="nav-number">3.7.</span> <span class="nav-text">ReentrantLock(可重入锁)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#reentrantlock%E4%BA%86%E8%A7%A3%E5%90%97%E6%98%AF%E5%85%AC%E5%B9%B3%E9%94%81%E5%90%97"><span class="nav-number">3.7.1.</span> <span class="nav-text">ReentrantLock了解吗？是公平锁吗？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#synchronized%E4%B8%8Ereentrantlock%E7%9A%84%E5%BC%82%E5%90%8C"><span class="nav-number">3.7.2.</span> <span class="nav-text">synchronized与ReentrantLock的异同</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java%E5%B9%B6%E5%8F%91%E5%85%B3%E9%94%AE%E5%AD%97"><span class="nav-number">3.8.</span> <span class="nav-text">Java并发关键字</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#volatile"><span class="nav-number">3.8.1.</span> <span class="nav-text">volatile</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#countdownlatch%E5%80%92%E8%AE%A1%E6%97%B6%E5%99%A8"><span class="nav-number">3.8.2.</span> <span class="nav-text">CountDownLatch(倒计时器)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#cyclicbarrier%E5%BE%AA%E7%8E%AF%E6%A0%85%E6%A0%8F"><span class="nav-number">3.8.3.</span> <span class="nav-text">CyclicBarrier(循环栅栏)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#semaphore%E4%BF%A1%E5%8F%B7%E9%87%8F"><span class="nav-number">3.8.4.</span> <span class="nav-text">Semaphore(信号量)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#countdownlatch%E5%92%8Ccyclicbarrier%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">3.8.5.</span> <span class="nav-text">CountDownLatch和CyclicBarrier的区别</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#cas%E6%AF%94%E8%BE%83%E5%B9%B6%E4%BA%A4%E6%8D%A2"><span class="nav-number">3.9.</span> <span class="nav-text">CAS(比较并交换)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#cas%E5%8E%9F%E7%90%86"><span class="nav-number">3.9.1.</span> <span class="nav-text">CAS原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#cas%E7%9A%84%E7%89%B9%E6%80%A7-%E4%B9%90%E8%A7%82%E9%94%81"><span class="nav-number">3.9.2.</span> <span class="nav-text">CAS的特性-乐观锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#aba%E9%97%AE%E9%A2%98%E6%80%8E%E4%B9%88%E8%A7%A3%E5%86%B3"><span class="nav-number">3.9.3.</span> <span class="nav-text">ABA问题？怎么解决？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#aqs"><span class="nav-number">3.10.</span> <span class="nav-text">AQS</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#aqs%E7%9A%84%E5%8E%9F%E7%90%86"><span class="nav-number">3.10.1.</span> <span class="nav-text">AQS的原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#aqs%E5%85%B1%E4%BA%AB%E8%B5%84%E6%BA%90%E7%9A%84%E6%96%B9%E5%BC%8F%E7%8B%AC%E5%8D%A0%E5%BC%8F%E5%92%8C%E5%85%B1%E4%BA%AB%E5%BC%8F"><span class="nav-number">3.10.2.</span> <span class="nav-text">AQS共享资源的方式：独占式和共享式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#atomic%E5%8E%9F%E5%AD%90%E7%B1%BB%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">3.11.</span> <span class="nav-text">Atomic原子类(待补充)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BA%86%E8%A7%A3%E9%94%81%E7%9A%84%E4%BC%98%E5%8C%96%E5%90%97jdk1.6%E4%B9%8B%E5%90%8E%E5%AF%B9%E9%94%81%E8%BF%9B%E8%A1%8C%E4%BA%86%E5%A4%A7%E9%87%8F%E7%9A%84%E4%BC%98%E5%8C%96%E4%BD%A0%E4%BA%86%E8%A7%A3%E5%90%97"><span class="nav-number">3.12.</span> <span class="nav-text">了解锁的优化吗？JDK1.6之后对锁进行了大量的优化，你了解吗？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#java8%E6%96%B0%E7%89%B9%E6%80%A7"><span class="nav-number">3.13.</span> <span class="nav-text">Java8新特性</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#jvm"><span class="nav-number">4.</span> <span class="nav-text">JVM</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#jvm%E7%BB%93%E6%9E%84%E8%A7%84%E8%8C%83java-se-8"><span class="nav-number">4.1.</span> <span class="nav-text">JVM结构规范(Java SE 8)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#hospot-jvm%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B"><span class="nav-number">4.2.</span> <span class="nav-text">HoSpot JVM内存模型</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8%E7%BA%BF%E7%A8%8B%E7%A7%81%E6%9C%89%E6%97%A0%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA%E9%97%AE%E9%A2%98"><span class="nav-number">4.2.1.</span> <span class="nav-text">程序计数器(线程私有，无内存溢出问题)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88%E7%BA%BF%E7%A8%8B%E7%A7%81%E6%9C%89%E6%8F%8F%E8%BF%B0java%E6%96%B9%E6%B3%95%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B"><span class="nav-number">4.2.2.</span> <span class="nav-text">Java虚拟机栈(线程私有，描述Java方法的执行过程)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88%E7%BA%BF%E7%A8%8B%E7%A7%81%E6%9C%89"><span class="nav-number">4.2.3.</span> <span class="nav-text">本地方法栈(线程私有)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#java%E5%A0%86%E7%BA%BF%E7%A8%8B%E5%85%B1%E4%BA%AB"><span class="nav-number">4.2.4.</span> <span class="nav-text">Java堆(线程共享)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%85%83%E7%A9%BA%E9%97%B4%E7%BA%BF%E7%A8%8B%E5%85%B1%E4%BA%AB%E6%96%B9%E6%B3%95%E5%8C%BA%E5%9C%A8hotspot-jvm%E4%B8%AD%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="nav-number">4.2.5.</span> <span class="nav-text">元空间(线程共享，方法区在HotSpot JVM中的实现)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%9B%B4%E6%8E%A5%E5%86%85%E5%AD%98"><span class="nav-number">4.2.6.</span> <span class="nav-text">直接内存</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E6%95%B0%E6%8D%AE%E5%8C%BA"><span class="nav-number">4.2.7.</span> <span class="nav-text">运行时数据区</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E7%9A%84"><span class="nav-number">4.2.8.</span> <span class="nav-text">对象是如何创建的</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%E6%97%B6%E5%86%85%E5%AD%98%E6%98%AF%E5%A6%82%E4%BD%95%E5%88%86%E9%85%8D%E7%9A%84"><span class="nav-number">4.2.9.</span> <span class="nav-text">创建对象时内存是如何分配的</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#jvm%E7%9A%84%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6"><span class="nav-number">4.3.</span> <span class="nav-text">JVM的类加载机制</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="nav-number">4.3.1.</span> <span class="nav-text">类的生命周期</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#jvm%E7%9A%84%E7%B1%BB%E5%8A%A0%E8%BD%BD%E9%98%B6%E6%AE%B5"><span class="nav-number">4.3.2.</span> <span class="nav-text">JVM的类加载阶段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-number">4.3.3.</span> <span class="nav-text">类加载器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6"><span class="nav-number">4.3.4.</span> <span class="nav-text">双亲委派机制(*)</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E4%B8%AD%E7%9A%84%E7%88%B6%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E5%92%8C%E5%AD%90%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E6%98%AF%E7%BB%A7%E6%89%BF%E5%85%B3%E7%B3%BB%E5%90%97"><span class="nav-number">4.3.4.1.</span> <span class="nav-text">类加载器中的父类加载器和子类加载器是继承关系吗？</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6%E7%9A%84%E5%A5%BD%E5%A4%84"><span class="nav-number">4.3.4.2.</span> <span class="nav-text">双亲委派机制的好处</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%A6%82%E4%BD%95%E6%89%93%E7%A0%B4%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6"><span class="nav-number">4.3.4.3.</span> <span class="nav-text">如何打破双亲委派机制</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#hotspot-jvm%E5%A0%86"><span class="nav-number">4.4.</span> <span class="nav-text">HotSpot JVM堆</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#jvm%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%9A%84%E5%8E%9F%E7%90%86%E4%B8%8E%E7%AE%97%E6%B3%95"><span class="nav-number">4.5.</span> <span class="nav-text">JVM垃圾回收的原理与算法</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%A6%82%E4%BD%95%E7%A1%AE%E5%AE%9A%E5%9E%83%E5%9C%BE"><span class="nav-number">4.5.1.</span> <span class="nav-text">如何确定垃圾？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E6%B3%95"><span class="nav-number">4.5.1.1.</span> <span class="nav-text">(1)引用计数法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90"><span class="nav-number">4.5.1.2.</span> <span class="nav-text">(2)可达性分析</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#java%E4%B8%AD%E7%9A%84%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B"><span class="nav-number">4.5.2.</span> <span class="nav-text">Java中的四种引用类型</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95"><span class="nav-number">4.5.3.</span> <span class="nav-text">垃圾回收算法</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%A0%87%E8%AE%B0-%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95mark-sweep"><span class="nav-number">4.5.3.1.</span> <span class="nav-text">(1)标记-清除算法(Mark-Sweep)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95coping"><span class="nav-number">4.5.3.2.</span> <span class="nav-text">(2)复制算法(Coping)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95mark-compact"><span class="nav-number">4.5.3.3.</span> <span class="nav-text">(3)标记整理算法(Mark-Compact)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%88%86%E4%BB%A3%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95generational-collecting"><span class="nav-number">4.5.3.4.</span> <span class="nav-text">(4)分代回收算法(Generational Collecting)</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8"><span class="nav-number">4.5.4.</span> <span class="nav-text">垃圾回收器</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#serial%E5%9B%9E%E6%94%B6%E5%99%A8%E5%8D%95%E7%BA%BF%E7%A8%8B%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95"><span class="nav-number">4.5.4.1.</span> <span class="nav-text">Serial回收器：单线程，复制算法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#parnew%E5%9B%9E%E6%94%B6%E5%99%A8%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95"><span class="nav-number">4.5.4.2.</span> <span class="nav-text">ParNew回收器：多线程，复制算法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#parallel-scavenge%E5%9B%9E%E6%94%B6%E5%99%A8%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%A4%8D%E5%88%B6%E7%AE%97%E6%B3%95"><span class="nav-number">4.5.4.3.</span> <span class="nav-text">Parallel Scavenge回收器：多线程，复制算法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#serial-old%E5%9B%9E%E6%94%B6%E5%99%A8%E5%8D%95%E7%BA%BF%E7%A8%8B%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95"><span class="nav-number">4.5.4.4.</span> <span class="nav-text">Serial Old回收器:单线程，标记整理算法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#parallel-old%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95"><span class="nav-number">4.5.4.5.</span> <span class="nav-text">Parallel Old垃圾回收器：多线程，标记整理算法。</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#cms%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8"><span class="nav-number">4.5.4.6.</span> <span class="nav-text">CMS垃圾回收器</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#g1%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8"><span class="nav-number">4.5.4.7.</span> <span class="nav-text">G1垃圾回收器</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#jvm%E5%90%AF%E5%8A%A8%E5%8F%82%E6%95%B0%E8%AE%BE%E7%BD%AE"><span class="nav-number">4.6.</span> <span class="nav-text">JVM启动参数设置</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A6%82%E4%BD%95%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E7%B1%BB%E6%98%AF%E6%97%A0%E7%94%A8%E7%9A%84%E7%B1%BB"><span class="nav-number">4.7.</span> <span class="nav-text">如何判断一个类是无用的类？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9C%A8%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E5%8F%AF%E8%83%BD%E4%BC%9A%E5%87%BA%E7%8E%B0oom%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">4.8.</span> <span class="nav-text">在什么情况下可能会出现OOM?(待补充)</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#mysql"><span class="nav-number">5.</span> <span class="nav-text">MySQL</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#mysql%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B"><span class="nav-number">5.1.</span> <span class="nav-text">MySQL数据类型</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#decimal%E5%92%8Cfloatdouble%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88"><span class="nav-number">5.1.1.</span> <span class="nav-text">decimal和float&#x2F;double的区别是什么？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mysql%E5%B8%B8%E7%94%A8%E7%9A%84%E5%AD%98%E5%82%A8%E5%BC%95%E6%93%8E"><span class="nav-number">5.2.</span> <span class="nav-text">MySQL常用的存储引擎</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%89%E8%8C%83%E5%BC%8F"><span class="nav-number">5.3.</span> <span class="nav-text">数据库三范式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%B4%A2%E5%BC%95"><span class="nav-number">5.4.</span> <span class="nav-text">索引(*)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B4%A2%E5%BC%95%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">5.4.1.</span> <span class="nav-text">索引的优缺点</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#mysql%E7%B4%A2%E5%BC%95%E7%9A%84%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%BB%B4%E5%BA%A6"><span class="nav-number">5.4.2.</span> <span class="nav-text">MySQL索引的类型有哪些(数据结构维度)？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B8%B8%E8%A7%81%E7%9A%84%E7%B4%A2%E5%BC%95%E6%9C%89%E5%93%AA%E4%BA%9B%E5%BA%94%E7%94%A8%E7%BB%B4%E5%BA%A6"><span class="nav-number">5.4.3.</span> <span class="nav-text">常见的索引有哪些(应用维度)？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%81%9A%E7%B0%87%E7%B4%A2%E5%BC%95%E5%92%8C%E9%9D%9E%E8%81%9A%E7%B0%87%E7%B4%A2%E5%BC%95%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">5.4.4.</span> <span class="nav-text">聚簇索引和非聚簇索引的区别</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%9D%9E%E8%81%9A%E7%B0%87%E7%B4%A2%E5%BC%95%E4%B8%80%E5%AE%9A%E4%BC%9A%E5%9B%9E%E8%A1%A8%E6%9F%A5%E8%AF%A2%E5%90%97"><span class="nav-number">5.4.5.</span> <span class="nav-text">非聚簇索引一定会回表查询吗？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#innodb%E4%B8%AD%E7%9A%84%E7%B4%A2%E5%BC%95%E6%96%B9%E6%A1%88"><span class="nav-number">5.4.6.</span> <span class="nav-text">InnoDB中的索引方案</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B4%A2%E5%BC%95%E8%A6%86%E7%9B%96"><span class="nav-number">5.4.7.</span> <span class="nav-text">索引覆盖</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%81%94%E5%90%88%E7%B4%A2%E5%BC%95"><span class="nav-number">5.4.8.</span> <span class="nav-text">联合索引</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BA%8B%E5%8A%A1"><span class="nav-number">5.5.</span> <span class="nav-text">事务</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BA%8B%E5%8A%A1%E7%9A%84%E7%89%B9%E6%80%A7"><span class="nav-number">5.5.1.</span> <span class="nav-text">事务的特性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B9%B6%E5%8F%91%E4%BA%8B%E5%8A%A1%E5%B8%A6%E6%9D%A5%E4%BA%86%E5%93%AA%E4%BA%9B%E9%97%AE%E9%A2%98"><span class="nav-number">5.5.2.</span> <span class="nav-text">并发事务带来了哪些问题？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">5.5.3.</span> <span class="nav-text">数据库的隔离级别有哪些？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E7%9A%84"><span class="nav-number">5.5.4.</span> <span class="nav-text">隔离级别是如何实现的？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#mvcc%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">5.5.5.</span> <span class="nav-text">MVCC(待补充)?</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93%E9%94%81"><span class="nav-number">5.6.</span> <span class="nav-text">数据库锁</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93%E9%94%81%E7%9A%84%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">5.6.1.</span> <span class="nav-text">数据库锁的类型有哪些？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#innodb%E6%9C%89%E5%93%AA%E5%87%A0%E7%B1%BB%E8%A1%8C%E9%94%81"><span class="nav-number">5.6.2.</span> <span class="nav-text">InnoDB有哪几类行锁？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E4%B9%90%E8%A7%82%E9%94%81%E5%92%8C%E6%82%B2%E8%A7%82%E9%94%81%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0"><span class="nav-number">5.6.3.</span> <span class="nav-text">什么是数据库的乐观锁和悲观锁？如何实现？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mysql%E6%97%A5%E5%BF%97%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">5.7.</span> <span class="nav-text">MySQL日志(待补充)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mysql%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">5.8.</span> <span class="nav-text">MySQL基础知识(待补充)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#mysql%E7%9A%84%E5%87%A0%E7%A7%8D%E8%BF%9E%E6%8E%A5%E6%9F%A5%E8%AF%A2%E5%BE%85%E5%AE%8C%E5%96%84"><span class="nav-number">5.8.1.</span> <span class="nav-text">MySQL的几种连接查询(待完善)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BC%98%E5%8C%96"><span class="nav-number">5.9.</span> <span class="nav-text">数据库优化</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%A4%A7%E8%A1%A8%E5%A6%82%E4%BD%95%E4%BC%98%E5%8C%96"><span class="nav-number">5.9.1.</span> <span class="nav-text">大表如何优化？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E5%88%86%E5%BA%93"><span class="nav-number">5.9.2.</span> <span class="nav-text">什么是分库？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E5%88%86%E8%A1%A8"><span class="nav-number">5.9.3.</span> <span class="nav-text">什么是分表？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E9%9C%80%E8%A6%81%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8"><span class="nav-number">5.9.4.</span> <span class="nav-text">什么情况下需要分库分表？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E5%90%8Eid%E9%94%AE%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86"><span class="nav-number">5.9.5.</span> <span class="nav-text">分库分表后，ID键如何处理？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#mysql%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%E5%8E%9F%E7%90%86"><span class="nav-number">5.9.6.</span> <span class="nav-text">MySQL主从复制原理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB"><span class="nav-number">5.9.7.</span> <span class="nav-text">什么是读写分离？</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#redis"><span class="nav-number">6.</span> <span class="nav-text">Redis</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFredis"><span class="nav-number">6.1.</span> <span class="nav-text">什么是Redis?</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E7%9A%84%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">6.2.</span> <span class="nav-text">Redis的优缺点？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E5%8D%95%E7%BA%BF%E7%A8%8B%E5%BF%AB%E7%9A%84%E5%8E%9F%E5%9B%A0"><span class="nav-number">6.3.</span> <span class="nav-text">Redis单线程快的原因</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E7%9A%84%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="nav-number">6.4.</span> <span class="nav-text">Redis的应用场景</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E6%98%AF%E5%8D%95%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%A6%82%E4%BD%95%E6%8F%90%E9%AB%98cpu%E7%9A%84%E5%88%A9%E7%94%A8%E7%8E%87"><span class="nav-number">6.5.</span> <span class="nav-text">Redis是单线程的，如何提高CPU的利用率</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E7%9A%84%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">6.6.</span> <span class="nav-text">Redis的数据类型有哪些?</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#zset%E7%9A%84%E5%BA%95%E5%B1%82%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E6%98%AF%E4%BB%80%E4%B9%88%E8%B7%B3%E8%A1%A8%E4%BA%86%E8%A7%A3%E5%90%97"><span class="nav-number">6.6.1.</span> <span class="nav-text">ZSet的底层数据结构是什么？跳表了解吗？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E9%94%AE%E8%BF%87%E6%9C%9F%E7%9A%84%E5%88%A0%E9%99%A4%E7%AD%96%E7%95%A5"><span class="nav-number">6.7.</span> <span class="nav-text">Redis键过期的删除策略</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%94%AE%E7%9A%84%E8%BF%87%E6%9C%9F%E5%88%A0%E9%99%A4%E7%AD%96%E7%95%A5"><span class="nav-number">6.7.1.</span> <span class="nav-text">键的过期删除策略</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E7%9A%84%E5%86%85%E5%AD%98%E6%B7%98%E6%B1%B0%E6%9C%BA%E5%88%B6"><span class="nav-number">6.7.2.</span> <span class="nav-text">Redis的内存淘汰机制？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E6%8C%81%E4%B9%85%E5%8C%96%E7%AD%96%E7%95%A5%E4%BB%A5%E5%8F%8A%E4%BC%98%E7%BC%BA%E7%82%B9"><span class="nav-number">6.8.</span> <span class="nav-text">Redis持久化策略以及优缺点</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#rdbredis-database"><span class="nav-number">6.8.1.</span> <span class="nav-text">(1)RDB(Redis Database)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#aofappend-only-file"><span class="nav-number">6.8.2.</span> <span class="nav-text">(2)AOF(Append-only File)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E7%9A%84%E4%BA%8B%E5%8A%A1"><span class="nav-number">6.9.</span> <span class="nav-text">Redis的事务</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFredis%E7%9A%84%E4%BA%8B%E5%8A%A1"><span class="nav-number">6.9.1.</span> <span class="nav-text">什么是Redis的事务</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E4%BA%8B%E5%8A%A1%E7%9A%84%E7%9B%B8%E5%85%B3%E5%91%BD%E4%BB%A4"><span class="nav-number">6.9.2.</span> <span class="nav-text">Redis事务的相关命令</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E4%BA%8B%E5%8A%A1%E6%89%A7%E8%A1%8C%E7%9A%84%E4%B8%89%E4%B8%AA%E9%98%B6%E6%AE%B5"><span class="nav-number">6.9.3.</span> <span class="nav-text">Redis事务执行的三个阶段</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E4%BA%8B%E5%8A%A1%E7%9A%84%E7%89%B9%E6%80%A7"><span class="nav-number">6.9.4.</span> <span class="nav-text">Redis事务的特性</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E9%9B%86%E7%BE%A4"><span class="nav-number">6.10.</span> <span class="nav-text">Redis集群</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E9%9B%86%E7%BE%A4%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">6.10.1.</span> <span class="nav-text">Redis集群的实现方案有哪些？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E7%9A%84%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%E6%9C%BA%E5%88%B6"><span class="nav-number">6.10.2.</span> <span class="nav-text">Redis的主从复制机制</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E4%B8%BB%E4%BB%8E%E9%9B%86%E7%BE%A4"><span class="nav-number">6.10.3.</span> <span class="nav-text">Redis主从集群</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E5%93%A8%E5%85%B5%E9%9B%86%E7%BE%A4"><span class="nav-number">6.10.4.</span> <span class="nav-text">Redis哨兵集群</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#redis-cluster%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">6.10.5.</span> <span class="nav-text">Redis Cluster(待补充)</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#redis%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E9%97%AE%E9%A2%98"><span class="nav-number">6.11.</span> <span class="nav-text">Redis的分布式问题</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#redis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81"><span class="nav-number">6.11.1.</span> <span class="nav-text">Redis实现分布式锁</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#setnx%E5%91%BD%E4%BB%A4"><span class="nav-number">6.11.1.1.</span> <span class="nav-text">(1)setnx命令</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#redisson%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">6.11.1.2.</span> <span class="nav-text">(2)Redisson分布式锁(待补充)</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9%E6%80%8E%E4%B9%88%E9%81%BF%E5%85%8D"><span class="nav-number">6.11.2.</span> <span class="nav-text">缓存穿透、缓存击穿、缓存雪崩，怎么避免？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">6.12.</span> <span class="nav-text">...(待补充)</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#spring"><span class="nav-number">7.</span> <span class="nav-text">Spring</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFspring"><span class="nav-number">7.1.</span> <span class="nav-text">什么是Spring?</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#spring%E7%94%B1%E5%93%AA%E4%BA%9B%E6%A8%A1%E5%9D%97%E7%BB%84%E6%88%90"><span class="nav-number">7.2.</span> <span class="nav-text">Spring由哪些模块组成？</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#core-container"><span class="nav-number">7.2.1.</span> <span class="nav-text">Core Container</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#aop"><span class="nav-number">7.2.2.</span> <span class="nav-text">AOP</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#data-accessintegration"><span class="nav-number">7.2.3.</span> <span class="nav-text">Data Access&#x2F;Integration</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#spring-web"><span class="nav-number">7.2.4.</span> <span class="nav-text">Spring Web</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#messaging"><span class="nav-number">7.2.5.</span> <span class="nav-text">Messaging</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#spring-test"><span class="nav-number">7.2.6.</span> <span class="nav-text">Spring Test</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#spring-ioc%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC"><span class="nav-number">7.3.</span> <span class="nav-text">Spring IoC(控制反转)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFioc"><span class="nav-number">7.3.1.</span> <span class="nav-text">什么是IoC?</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%ACioc%E7%9A%84%E4%BD%9C%E7%94%A8%E6%98%AF%E4%BB%80%E4%B9%88"><span class="nav-number">7.3.2.</span> <span class="nav-text">控制反转(IoC)的作用是什么？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#spring-ioc%E7%9A%84%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6"><span class="nav-number">7.3.3.</span> <span class="nav-text">Spring IoC的实现机制</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#beanfactory%E5%92%8Capplicationcontext%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB"><span class="nav-number">7.3.4.</span> <span class="nav-text">BeanFactory和ApplicationContext有什么区别？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AF%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E7%9A%84%E6%96%B9%E5%BC%8F%E6%9C%89%E5%93%AA%E4%BA%9B%E8%A1%A5%E5%85%85"><span class="nav-number">7.3.5.</span> <span class="nav-text">什么是依赖注入，依赖注入的方式有哪些？(补充)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%B0%86%E4%B8%80%E4%B8%AA%E7%B1%BB%E5%A3%B0%E6%98%8E%E4%B8%BAbean%E7%9A%84%E6%B3%A8%E8%A7%A3%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">7.3.6.</span> <span class="nav-text">将一个类声明为Bean的注解有哪些？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#component%E5%92%8Cbean%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88"><span class="nav-number">7.3.7.</span> <span class="nav-text">@Component和@Bean的区别是什么？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%B3%A8%E5%85%A5bean%E7%9A%84%E6%B3%A8%E8%A7%A3%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">7.3.8.</span> <span class="nav-text">注入Bean的注解有哪些？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#autowired%E5%92%8Cresource%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88"><span class="nav-number">7.3.9.</span> <span class="nav-text">@Autowired和@Resource的区别是什么？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#bean%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">7.3.10.</span> <span class="nav-text">Bean的作用域有哪些？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#bean%E6%98%AF%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84%E5%90%97"><span class="nav-number">7.3.11.</span> <span class="nav-text">Bean是线程安全的吗？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="nav-number">7.3.12.</span> <span class="nav-text">Bean的生命周期</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#spring-aop%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B"><span class="nav-number">7.4.</span> <span class="nav-text">Spring AOP(面向切面编程)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFspring-aop"><span class="nav-number">7.4.1.</span> <span class="nav-text">什么是Spring AOP?</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#aop%E7%9A%84%E4%B8%80%E4%BA%9B%E5%90%8D%E8%AF%8D"><span class="nav-number">7.4.2.</span> <span class="nav-text">AOP的一些名词</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#aspectj%E5%AE%9A%E4%B9%89%E7%9A%84%E9%80%9A%E7%9F%A5%E7%B1%BB%E5%9E%8B%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">7.4.3.</span> <span class="nav-text">AspectJ定义的通知类型有哪些？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%A4%9A%E4%B8%AA%E5%88%87%E9%9D%A2%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F%E5%A6%82%E4%BD%95%E6%8E%A7%E5%88%B6"><span class="nav-number">7.4.4.</span> <span class="nav-text">多个切面的执行顺序如何控制？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#spring-aop%E7%9A%84%E4%B8%A4%E7%A7%8D%E4%BB%A3%E7%90%86%E6%96%B9%E5%BC%8F"><span class="nav-number">7.4.5.</span> <span class="nav-text">Spring AOP的两种代理方式</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86"><span class="nav-number">7.4.5.1.</span> <span class="nav-text">JDK动态代理</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#cglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">7.4.5.2.</span> <span class="nav-text">CGLib动态代理(待补充)</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E5%92%8Ccglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="nav-number">7.4.5.3.</span> <span class="nav-text">JDK动态代理和CGLib动态代理的区别</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#spring%E4%BA%8B%E5%8A%A1%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">7.5.</span> <span class="nav-text">Spring事务(待补充)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#spring%E4%B8%AD%E7%94%A8%E5%88%B0%E4%BA%86%E5%93%AA%E4%BA%9B%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F"><span class="nav-number">7.6.</span> <span class="nav-text">Spring中用到了哪些设计模式？</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#springmvc"><span class="nav-number">8.</span> <span class="nav-text">SpringMVC</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%AF%B9springmvc%E7%9A%84%E7%90%86%E8%A7%A3"><span class="nav-number">8.1.</span> <span class="nav-text">对SpringMVC的理解</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#springmvc%E7%9A%84%E6%A0%B8%E5%BF%83%E7%BB%84%E4%BB%B6%E6%9C%89%E5%93%AA%E4%BA%9B"><span class="nav-number">8.2.</span> <span class="nav-text">SpringMVC的核心组件有哪些？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#springmvc%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86"><span class="nav-number">8.3.</span> <span class="nav-text">SpringMVC工作原理</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E6%80%8E%E4%B9%88%E5%81%9A"><span class="nav-number">8.4.</span> <span class="nav-text">统一异常处理怎么做？</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#mybatis"><span class="nav-number">9.</span> <span class="nav-text">Mybatis</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%92%8C%E7%9A%84%E5%8C%BA%E5%88%AB%E6%98%AF%E4%BB%80%E4%B9%88"><span class="nav-number">9.1.</span> <span class="nav-text">#{}和${}的区别是什么？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#dao%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E6%98%AF%E4%BB%80%E4%B9%88%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">9.2.</span> <span class="nav-text">Dao接口的工作原理是什么？(待补充)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mybatis%E6%98%AF%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8C%E5%88%86%E9%A1%B5%E7%9A%84%E5%88%86%E9%A1%B5%E6%8F%92%E4%BB%B6%E7%9A%84%E5%8E%9F%E7%90%86%E6%98%AF%E4%BB%80%E4%B9%88"><span class="nav-number">9.3.</span> <span class="nav-text">Mybatis是如何进行分页的？分页插件的原理是什么？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%AE%80%E8%BF%B0mybatis%E7%9A%84%E6%8F%92%E4%BB%B6%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86%E4%BB%A5%E5%8F%8A%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99%E4%B8%80%E4%B8%AA%E6%8F%92%E4%BB%B6"><span class="nav-number">9.4.</span> <span class="nav-text">简述MyBatis的插件运行原理，以及如何编写一个插件</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mybatis%E5%8A%A8%E6%80%81sql%E6%98%AF%E5%81%9A%E4%BB%80%E4%B9%88%E7%9A%84%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9B%E5%8A%A8%E6%80%81sql"><span class="nav-number">9.5.</span> <span class="nav-text">Mybatis动态sql是做什么的？都有哪些动态sql？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mybatis%E6%98%AF%E5%90%A6%E6%94%AF%E6%8C%81%E5%BB%B6%E8%BF%9F%E5%8A%A0%E8%BD%BD%E5%AE%83%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E6%98%AF%E4%BB%80%E4%B9%88"><span class="nav-number">9.6.</span> <span class="nav-text">Mybatis是否支持延迟加载？它的实现原理是什么？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mybatis%E9%83%BD%E6%9C%89%E5%93%AA%E4%BA%9Bexecutor%E6%89%A7%E8%A1%8C%E5%99%A8"><span class="nav-number">9.7.</span> <span class="nav-text">Mybatis都有哪些Executor执行器？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%AF%B4mybatis%E6%98%AF%E5%8D%8A%E8%87%AA%E5%8A%A8orm%E6%98%A0%E5%B0%84%E5%B7%A5%E5%85%B7"><span class="nav-number">9.8.</span> <span class="nav-text">为什么说Mybatis是半自动ORM映射工具？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#mybatisplus%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">9.9.</span> <span class="nav-text">MybatisPlus(待补充)</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#springboot"><span class="nav-number">10.</span> <span class="nav-text">SpringBoot</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#springcloud"><span class="nav-number">11.</span> <span class="nav-text">SpringCloud</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#springsecurity"><span class="nav-number">12.</span> <span class="nav-text">SpringSecurity</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%88%86%E5%B8%83%E5%BC%8F%E7%9B%B8%E5%85%B3"><span class="nav-number">13.</span> <span class="nav-text">分布式相关</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#rabbitmq"><span class="nav-number">14.</span> <span class="nav-text">RabbitMQ</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%BC%95%E5%85%A5%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E5%91%A2%E4%B8%BE%E5%87%A0%E4%B8%AA%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">14.1.</span> <span class="nav-text">为什么要引入消息队列呢？举几个应用场景(待补充)</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#docker"><span class="nav-number">15.</span> <span class="nav-text">Docker</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#elastissearch"><span class="nav-number">16.</span> <span class="nav-text">ElastisSearch</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F"><span class="nav-number">17.</span> <span class="nav-text">设计模式</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#spring%E4%B8%AD%E7%94%A8%E5%88%B0%E4%BA%86%E5%93%AA%E4%BA%9B%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-1"><span class="nav-number">17.1.</span> <span class="nav-text">Spring中用到了哪些设计模式？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F"><span class="nav-number">17.2.</span> <span class="nav-text">单例模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F"><span class="nav-number">17.3.</span> <span class="nav-text">工厂模式</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C"><span class="nav-number">18.</span> <span class="nav-text">计算机网络</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#osi%E4%B8%83%E5%B1%82%E6%A8%A1%E5%9E%8B"><span class="nav-number">18.1.</span> <span class="nav-text">OSI七层模型</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#tcpip%E5%9B%9B%E5%B1%82%E6%A8%A1%E5%9E%8B"><span class="nav-number">18.2.</span> <span class="nav-text">TCP&#x2F;IP四层模型</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#tcp%E5%92%8Cudp%E7%9A%84%E5%8C%BA%E5%88%AB%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="nav-number">18.3.</span> <span class="nav-text">TCP和UDP的区别?应用场景？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#tcp%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B"><span class="nav-number">18.4.</span> <span class="nav-text">TCP三次握手</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#tcp%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B"><span class="nav-number">18.5.</span> <span class="nav-text">TCP四次挥手</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#tcp%E5%A6%82%E4%BD%95%E4%BF%9D%E8%AF%81%E4%BC%A0%E8%BE%93%E7%9A%84%E5%8F%AF%E9%9D%A0%E6%80%A7%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">18.6.</span> <span class="nav-text">TCP如何保证传输的可靠性(待补充)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%94%AE%E5%85%A5%E7%BD%91%E5%9D%80%E5%88%B0%E7%BD%91%E9%A1%B5%E6%98%BE%E7%A4%BA%E6%9C%9F%E9%97%B4%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88%E5%BE%85%E8%A1%A5%E5%85%85"><span class="nav-number">18.7.</span> <span class="nav-text">键入网址到网页显示，期间发生了什么？(待补充)</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F"><span class="nav-number">19.</span> <span class="nav-text">操作系统</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5"><span class="nav-number">20.</span> <span class="nav-text">参考链接</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="别团等shy哥发育"
      src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/avatar.jpg">
  <p class="site-author-name" itemprop="name">别团等shy哥发育</p>
  <div class="site-description" itemprop="description">别团等shy哥发育的博客</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/hexo/archives/">
          <span class="site-state-item-count">17</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/hexo/categories/">
        <span class="site-state-item-count">4</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/hexo/tags/">
        <span class="site-state-item-count">19</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/xiongtete0519" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;xiongtete0519" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="/hexo/xiongtete0519@gmail.com" title="E-Mail → xiongtete0519@gmail.com" rel="noopener me"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://codeleader.blog.csdn.net/" title="CSDN → https:&#x2F;&#x2F;codeleader.blog.csdn.net&#x2F;" rel="noopener me" target="_blank"><i class="fa CSDN fa-fw"></i>CSDN</a>
      </span>
  </div>

        </div>
      </div>
        <div class="back-to-top animated" role="button" aria-label="返回顶部">
          <i class="fa fa-arrow-up"></i>
          <span>0%</span>
        </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://codeleader.gitee.io/hexo/2023/08/11/%E9%9D%A2%E8%AF%95%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://codeleader.oss-cn-beijing.aliyuncs.com/site/avatar.jpg">
      <meta itemprop="name" content="别团等shy哥发育">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="CodeLeader">
      <meta itemprop="description" content="别团等shy哥发育的博客">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="面试知识点总结(持续更新中) | CodeLeader">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          面试知识点总结(持续更新中)
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-08-11 22:51:12" itemprop="dateCreated datePublished" datetime="2023-08-11T22:51:12+08:00">2023-08-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-12-16 13:53:51" itemprop="dateModified" datetime="2023-12-16T13:53:51+08:00">2023-12-16</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/hexo/categories/%E9%9D%A2%E7%BB%8F/" itemprop="url" rel="index"><span itemprop="name">面经</span></a>
        </span>
    </span>

  
    <span class="post-meta-item" title="阅读次数" id="busuanzi_container_page_pv">
      <span class="post-meta-item-icon">
        <i class="far fa-eye"></i>
      </span>
      <span class="post-meta-item-text">阅读次数：</span>
      <span id="busuanzi_value_page_pv"></span>
    </span>
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>41k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>2:29</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <h1 id="java基础">Java基础</h1>
<h2 id="equals和hashcode的区别"><code>==</code>，equals和hashCode的区别</h2>
<ul>
<li><code>==</code>:对于基本数据类型,<code>==</code>比较的是值，对于引用数据类型，<code>==</code>比较的是内存地址。</li>
<li><code>equals</code>:对于没有重写<code>equals</code>方法的类，<code>equals</code>方法和<code>==</code>作用类似；对于重写过<code>equals</code>方法的类，<code>equals</code>比较的是值。</li>
</ul>
<p><code>hashCode</code>方法返回对象的散列码，返回值是int类型。散列码的作用是确定对象在哈希表中的索引位置。</p>
<blockquote>
<p>两个对象相等，则<code>hashCode</code>一定相同。</p>
<p>两个对象有相同的<code>hashCode</code>值，它们不一定相等。</p>
<p><code>hashCode()</code>方法默认是对堆上的对象产生独特值，如果没有重写<code>hashCode()</code>方法，则该类的两个对象的<code>hashCode</code>值肯定不同。</p>
</blockquote>
<h2 id="stringstringbufferstringbuilder区别">String、<code>StringBuffer</code>、<code>StringBuilder</code>区别</h2>
<ul>
<li>String类的对象是不可变的，所以线程安全，只要修改就会产生新的对象，所以频繁拼接时空消耗很大。</li>
<li><code>StringBuffer</code>和<code>StringBuilder</code>都是在原对象上操作，<code>StringBuffer</code>是线程安全的，<code>StringBuilder</code>线程不安全。</li>
</ul>
<table>
<thead>
<tr class="header">
<th style="text-align: center;">字符串</th>
<th style="text-align: center;">是否可变</th>
<th style="text-align: center;">是否安全</th>
<th style="text-align: center;">性能</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">String</td>
<td style="text-align: center;">不可变</td>
<td style="text-align: center;">安全</td>
<td style="text-align: center;">低</td>
</tr>
<tr class="even">
<td style="text-align: center;"><code>StringBuffer</code></td>
<td style="text-align: center;">可变</td>
<td style="text-align: center;">安全</td>
<td style="text-align: center;">较高</td>
</tr>
<tr class="odd">
<td style="text-align: center;"><code>StringBuilder</code></td>
<td style="text-align: center;">可变</td>
<td style="text-align: center;">不安全</td>
<td style="text-align: center;">高</td>
</tr>
</tbody>
</table>
<blockquote>
<p>性能：<code>StringBuilder</code>&gt;<code>StringBuffer</code>&gt;<code>String</code></p>
</blockquote>
<h3 id="string设计为不可变的好处有哪些">String设计为不可变的好处有哪些？</h3>
<p>不可变使得String类在多线程环境下更安全，因为多个线程可以同时访问和共享同一个String对象，而不需要担心其被修改。</p>
<p>优化内存，相同的String对象可以被多个引用共享，而不需要产生额外的副本。</p>
<blockquote>
<p><strong>String为什么不可变？</strong></p>
<p>String类被final修饰，杜绝被子类覆盖的问题，String底层使用字符数组实现，而且该字符数组被final修饰，且没有暴露任何修改字符数字的方法。</p>
</blockquote>
<h2 id="字符串常量池">字符串常量池</h2>
<p>字符串常量池位于堆内存中，专门用来存储字符串常量，可以提高内存的使用率，避免开辟多块空间存储相同的字符串，在创建字符串时JVM首先会检查字符串常量池，如果池中有该字符串，则返回它的引用，如果不存在，则实例化一个字符串放到池中，并返回其引用。</p>
<blockquote>
<p>字符串存放位置：如果使⽤常量的⽅式，该对象将被存储在常量池；如果使⽤new的⽅式，该对象将被存储在堆中。</p>
</blockquote>
<h2 id="string的intern方法">String的<code>intern</code>方法</h2>
<p><code>String.intern()</code>是一个native(本地)方法，其作用是将指定字符串对象的引用保存到字符串常量池中。</p>
<ul>
<li>如果字符串常量池中保存了对应的字符串对象的引用，就直接返回该引用。</li>
<li>如果字符串常量池中没有保存对应的字符串对象的引用，那就在常量池中创建一个指向该字符串对象的引用并返回。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在堆中创建字符串对象”Java“</span></span><br><span class="line"><span class="comment">// 将字符串对象”Java“的引用保存在字符串常量池中</span></span><br><span class="line"><span class="type">String</span> <span class="variable">s1</span> <span class="operator">=</span> <span class="string">"Java"</span>;</span><br><span class="line"><span class="comment">// 直接返回字符串常量池中字符串对象”Java“对应的引用</span></span><br><span class="line"><span class="type">String</span> <span class="variable">s2</span> <span class="operator">=</span> s1.intern();</span><br><span class="line"><span class="comment">// 会在堆中在单独创建一个字符串对象</span></span><br><span class="line"><span class="type">String</span> <span class="variable">s3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(<span class="string">"Java"</span>);</span><br><span class="line"><span class="comment">// 直接返回字符串常量池中字符串对象”Java“对应的引用</span></span><br><span class="line"><span class="type">String</span> <span class="variable">s4</span> <span class="operator">=</span> s3.intern();</span><br><span class="line"><span class="comment">// s1 和 s2 指向的是堆中的同一个对象</span></span><br><span class="line">System.out.println(s1 == s2); <span class="comment">// true</span></span><br><span class="line"><span class="comment">// s3 和 s4 指向的是堆中不同的对象</span></span><br><span class="line">System.out.println(s3 == s4); <span class="comment">// false</span></span><br><span class="line"><span class="comment">// s1 和 s4 指向的是堆中的同一个对象</span></span><br><span class="line">System.out.println(s1 == s4); <span class="comment">//true</span></span><br></pre></td></tr></table></figure>
<h2 id="浅拷贝与深拷贝">浅拷贝与深拷贝</h2>
<ul>
<li><strong>浅拷贝</strong>：对基本数据类型进行值传递，对引用数据类型复制一个引用指向原始引用的对象，就是复制的引用和原始引用指向同一个对象。</li>
<li><strong>深拷贝</strong>：对基本数据类型进行值传递，对引用数据类型，创建一个新的对象，并复制其内容，两个引用指向两个对象，但对象内容相同。</li>
</ul>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230629100728692.png" alt=""><figcaption>image-20230629100728692</figcaption>
</figure>
<h2 id="装箱与拆箱">装箱与拆箱</h2>
<ul>
<li><p><strong>装箱</strong>：将基本数据类型转换成包装类的过程。</p></li>
<li><p><strong>拆箱</strong>：将包装类转换成基本数据类型的过程。</p></li>
<li><p><strong>手动装箱</strong>：调用构造器/调用静态方法<code>valueOf</code></p></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//构造器</span></span><br><span class="line"><span class="type">int</span> intValue=<span class="number">100</span>;</span><br><span class="line">Integer obj=<span class="keyword">new</span> <span class="title class_">Integer</span>(intValue);</span><br><span class="line"><span class="comment">//静态方法</span></span><br><span class="line">Integer obj1=Integer.valueOf(intValue);</span><br></pre></td></tr></table></figure>
<ul>
<li><strong>手动拆箱</strong>：直接调用对应包装类的<code>xxValue()</code>方法即可。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Integer obj=<span class="keyword">new</span> <span class="title class_">Integer</span>(<span class="number">100</span>);</span><br><span class="line"><span class="type">int</span> value=obj.intValue();</span><br></pre></td></tr></table></figure>
<p><strong>自动装箱与自动拆箱</strong>(只能发生在对应的类型之间)</p>
<ul>
<li><p>自动装箱：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> intValue=<span class="number">100</span>;</span><br><span class="line">Integer obj=intValue;</span><br></pre></td></tr></table></figure></li>
<li><p>自动拆箱：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Integer obj=<span class="keyword">new</span> <span class="title class_">Integer</span>(<span class="number">100</span>);</span><br><span class="line"><span class="type">int</span> value=obj;</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="方法重写和重载的区别">方法重写和重载的区别？</h2>
<ul>
<li><strong>方法重载</strong>：发生在同一个类中，拥有两个或更多个名称相同但参数列表(参数类型、参数个数)不同的方法，方法的重载与返回值类型无关。</li>
<li><strong>方法重写</strong>：发生在当子类继承父类时，对父类中的一些方法根据自己需求进行重写操作。</li>
</ul>
<p>方法的重写发生在运行时，因为在编译时，编译器无法知道我们到底是调用的父类方法还是子类方法，只有在实际运行的时候才知道应该调用哪个方法，这也是Java运行时多态的体现。</p>
<p>方法的重载发生在编译时，在编译过程中，编译器必须根据参数类型以及长度来确定调用的哪个方法，这也是Java编译时多态的体现。</p>
<h2 id="java程序初始化顺序">Java程序初始化顺序</h2>
<p>父类静态变量-&gt;父类静态代码块-&gt;子类静态变量-&gt;子类静态代码-&gt;父类非静态变量-&gt;父类非静态代码块-&gt;父类构造方法-&gt;子类非静态变量-&gt;子类非静态代码块-&gt;子类构造方法</p>
<h2 id="io流">IO流</h2>
<ul>
<li><code>InputStream</code>:字节输入流，以字节的方式读取数据</li>
<li><code>OutputStream</code>:字节输出流，以字节的方式输出数据</li>
<li><code>Reader</code>:字符输入流，以字符的方式读取数据</li>
<li><code>Writer</code>:字符输出流，以字符的方式输出数据</li>
</ul>
<blockquote>
<p>字节流：直接处理二进制，1字节1字节处理，适用于一切数据集，包括纯文本、doc、xls、图片、音视频等。</p>
<p>字符流：1字符1字符处理，只能处理纯文本数据。</p>
</blockquote>
<h2 id="bionioaio的区别">BIO、NIO、AIO的区别</h2>
<p><code>BIO(Blocking I/O)</code>是最传统的同步阻塞IO模型，服务器端的实现是一个连接只有一个线程处理，线程在发起请求后，会等待连接返回。</p>
<p><code>NIO(Bonblocking I/O,非阻塞IO)</code>:服务器端实现模式为一个请求一个线程，即客户端发送的请求都会注册到多路复用器上，多路复用器轮询到连接有IO请求时才启动一个线程进行处理。</p>
<p><code>AIO(Asyncchronous I/O)</code>:服务器端实现模式为一个有效请求一个线程，客户端的IO请求都是由操作系统先完成了再通知服务器用其启动线程处理。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230706111234138.png" alt=""><figcaption>image-20230706111234138</figcaption>
</figure>
<h2 id="java序列化和反序列化">Java序列化和反序列化</h2>
<blockquote>
<p>主要目的：通过网络传输对象或者是将对象存储到文件系统、数据库、内存中。</p>
</blockquote>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/a478c74d-2c48-40ae-9374-87aacf05188c.png"></p>
<ul>
<li><p>序列化：将对象写入到IO流中。</p></li>
<li><p>反序列化：从IO流中恢复对象。</p></li>
<li><p>序列化的意义：将Java对象转换成字节数组，更加便于网络传输或存储在磁盘上，在需要时再将这些字节数组反序列化为对象。</p></li>
<li><p>实现方式：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(<span class="number">1</span>)实现Serializable接口</span><br><span class="line">(<span class="number">2</span>)实现Externalizable接口</span><br></pre></td></tr></table></figure></li>
</ul>
<blockquote>
<p>序列化注意事项：</p>
<ul>
<li>对象的类名、实例变量会被序列化；方法、类变量、<code>transient</code>实例变量都不会被序列化。</li>
<li>想让某个变量不被序列化，可以使用<code>transient</code>修饰。</li>
<li>序列化对象的引用成员变量也必须是可序列化的，否则会报错。</li>
<li>反序列化时必须有序列化对象的<code>class</code>文件。</li>
</ul>
</blockquote>
<h2 id="反射机制">反射机制</h2>
<p><strong>反射机制</strong>是在程序运行中，对任意一个类都能获取其所有属性和方法，并且对任意对象都能调用其任意方法。这种动态获取类和对象的信息，以及动态调用对象的方法的功能被称为Java的反射机制。</p>
<p>Java获取Class对象的三种方式</p>
<ul>
<li>调用某个对象的<code>getClass</code>方法</li>
<li>调用某个类的<code>class</code>属性</li>
<li>调用Class类中的<code>forName</code>静态方法</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Person p=<span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line"><span class="comment">//调用getClass方法</span></span><br><span class="line">Class c1=p.getClass();</span><br><span class="line"><span class="comment">//调用类的class属性</span></span><br><span class="line">Class c2=Person.class;</span><br><span class="line"><span class="comment">//调用Class类中的forName静态方法</span></span><br><span class="line">Class c3=Class.forName(<span class="string">"com.company"</span>)<span class="comment">//(完整包路径及名称)</span></span><br></pre></td></tr></table></figure>
<blockquote>
<p>因为一个类在<code>JVM</code>中只会有一个<code>Class</code>实例，所以对<code>c1、c2、c3</code>进行<code>equals</code>比较时返回的都是<code>true</code>。</p>
</blockquote>
<h2 id="java异常体系">Java异常体系</h2>
<p>Java中的异常主要分为<code>Error</code>和<code>Exception</code></p>
<ul>
<li><strong>Error</strong>:指的是Java虚拟机无法解决的严重问题，一般不编写针对性的代码进行处理。</li>
<li><strong>Exception</strong>：指Java程序运行异常，在运行中的程序发生了程序员不期望发生的事情，可以被Java异常处理机制处理。</li>
</ul>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230706112049155.png" alt=""><figcaption>image-20230706112049155</figcaption>
</figure>
<blockquote>
<ul>
<li><code>RuntimeException</code>:指在Java虚拟机正常运⾏期间抛出的异常，<code>RuntimeException</code>可以被捕获并处理，如果出现此情况，我们需要抛出异常或者捕获并处理异常。常见的有<code>NullPointerException</code>、(空指针异常)<code>ClassCastException</code>(类型转换异常)、 <code>ArrayIndexOutOfBoundsException</code>(数组越界异常)等。</li>
<li><code>CheckedException</code>:指在编译阶段Java编译器检查<code>CheckedException</code>异常，并强制程序捕获和处理此类异常，要求程序在可能出现异常的地⽅通过<code>try catch</code>语句块捕获异常并处理异常。常见的有由于I/O错误导致的<code>IOException</code>、<code>SQLException</code>、<code>ClassNotFoundException</code>等。该类异常通常由于打开错误的⽂件、<code>SQL</code>语法错误、类不存在等引起。</li>
</ul>
</blockquote>
<h3 id="异常的处理方式">异常的处理方式</h3>
<ul>
<li>抛出异常</li>
<li>使用try catch语句捕获异常</li>
</ul>
<p>不建议使用catch捕获Error,当程序出现Error的时候，通常出现了不可恢复的问题，通常是由JVM或者底层系统引起的，例如内存溢出(OutOfMemoryError)或者栈溢出(StackOverflowError)。</p>
<blockquote>
<p>多线程中一个线程出现异常不会影响到线程池里面其他线程的正常执行，这个线程不是被回收了而是线程池把这个线程移除掉，同时创建一个新的线程放到线程池中(参考别人的)。</p>
<p>参考：<a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/136571068#:~:text=%E4%BC%B0%E8%AE%A1%E5%BE%88%E5%A4%9A%E4%BA%BA%E4%BC%9A%E6%98%AF%E4%BB%A5%E4%B8%8B%E4%B8%89%E7%82%B9%E7%AD%94%E6%A1%88%EF%BC%88me%20too%29%3A,1.%E6%8A%9B%E5%BC%82%E5%B8%B8%E5%87%BA%E6%9D%A5%E5%B9%B6%E6%89%93%E5%8D%B0%E5%9C%A8%E6%8E%A7%E5%88%B6%E5%8F%B0%E4%B8%8A%202.%E5%85%B6%E4%BB%96%E7%BA%BF%E7%A8%8B%E4%BB%BB%E5%8A%A1%E4%B8%8D%E5%8F%97%E5%BD%B1%E5%93%8D%203.%E5%BC%82%E5%B8%B8%E7%BA%BF%E7%A8%8B%E4%BC%9A%E8%A2%AB%E5%9B%9E%E6%94%B6">70%人答不全！线程池中的一个线程异常了会被怎么处理?</a></p>
</blockquote>
<h2 id="java注解">Java注解</h2>
<p><strong>注解</strong>(<code>Annotation</code>)是Java提供的设置程序中元素的关联信息和元数据(<code>MetaData</code>)的方法，它是一个接口，程序可以通过反射获取指定程序中元素的注解对象，然后通过该注解对象获取注解中的元数据信息。</p>
<p>(1)<strong>系统预定义的基本注解</strong>：</p>
<ul>
<li><code>@Override</code>:标记某个方法是重写父类或父接口的方法，该注解只能标记在重写的方法上面。</li>
<li><code>@Deprecated</code>:标记某个程序元素(类、方法等)已过时。</li>
<li><code>@SuppressWarnings</code>:标记在需要抑制编译器警告的程序元素上。</li>
</ul>
<p><strong>(2)元注解</strong>：</p>
<ul>
<li><code>@Target</code>：限制注解的使用范围，即该注解可以用于哪些程序元素。</li>
<li><code>@Retention</code>:用来解释新声明注解的保留策略。</li>
<li><code>@Documented</code>:用于解释新声明注解用在某个包、类、方法等上面后，当使用<code>javadoc</code>工具提取文档注释生成的<code>API</code>文档时，是否将对应的注解信息也去读到<code>API</code>文档。</li>
<li><code>@Inherited</code>:用于解释新声明注解用在某个类、方法上面后是否可以被其子类继承。</li>
<li><code>@Repeatable</code>:Java 8新注解，引入重复注解机制，这样相同的注解可以在同一个地方使用多次，重复注解机制本身必须使用<code>@Repeatable</code>注解标记。</li>
</ul>
<p><strong>(3)自定义注解</strong>：</p>
<p>在Java中，注解被看作一种特殊的接口，使用<code>@interface</code>关键字进行声明。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">@元注解</span><br><span class="line">[修饰符] <span class="meta">@interface</span> 注解名{</span><br><span class="line">	返回值类型 方法名() <span class="keyword">default</span> 默认返回值</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(ElementType.METHOD)</span>	<span class="comment">//作用在方法上</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span><span class="comment">//保留到运行期间</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> MyAnnotation {</span><br><span class="line">    String <span class="title function_">value</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">"codeleader"</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>如果注解只有一个抽象方法，建议抽象方法名为value，value名的抽象方法在使用该注解时可以省略“value=”而直接返回值。</p>
</blockquote>
<blockquote>
<p>应用场景：注解的实现其实用到了反射，比如我们在使用SpringBoot的时候，一个<code>@Component</code>注解就声明了一个类为<code>Spring Bean</code>，一个<code>@Value</code>注解就读取到了配置文件中的值。</p>
</blockquote>
<h2 id="java泛型">Java泛型</h2>
<p><code>JDK1.5</code>引入了泛型的概念，让我们在程序中可以用某种方式标识完全未知的类型，使得程序顺利编写并通过编译，等到使用时再确定它的具体类型。泛型指的就是泛化的类型，即用<code>&lt;T&gt;</code>等形式来表示一个未确定的类型。</p>
<h3 id="泛型类或泛型接口">泛型类或泛型接口</h3>
<p>如果某个类或接口在声明时，在类名或接口名后面加了泛型，那么就称它为泛型类或泛型接口。<code>JDK1.5</code>把所有的集合类和接口都改写成了泛型类和泛型接口。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Iterator</span>&lt;E&gt;{</span><br><span class="line">	<span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span>;</span><br><span class="line">    E <span class="title function_">next</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="泛型上限">泛型上限</h3>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这里T只能为Number或其子类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</span>&lt;T <span class="keyword">extends</span> <span class="title class_">Number</span>&gt;{ </span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> T score;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="类型通配符">类型通配符</h3>
<p>类型通配符用一个<code>&lt;?&gt;</code>来表示，它代表任意引用数据类型。类型通配符只能出现在使用泛型类或泛型接口来声明变量或形参时。</p>
<p>类型通配符的上限:<code>&lt;? extends 上限&gt;</code></p>
<p>类型通配符的下限：<code>&lt;? super 下限&gt;</code></p>
<h3 id="泛型擦除">泛型擦除</h3>
<p>在编码阶段使用泛型时加上的类型参数，会被编译器在编译阶段去掉，这个过程叫做泛型擦除。</p>
<blockquote>
<p>泛型主要用于编译阶段。在编译后生成的<code>Java</code>字节码文件中不包含泛型中的类型信息。例如，在编码时定义的<code>List&lt;Integer&gt;</code>和<code>List&lt;String&gt;</code>经过编译后统一为List。<code>JVM</code>读取的只是List，由泛型附加的类型信息对<code>JVM</code>来说是不可见的。</p>
</blockquote>
<h2 id="comparable和comparator的区别">Comparable和Comparator的区别(*)</h2>
<p><code>Comparable</code>接口和<code>Comparator</code>都是Java中用于排序的接口，在实现类对象之间比较大小、排序等方面发挥了重要作用。</p>
<ul>
<li><code>Comparable</code>接口实际上是出自<code>java.lang</code>包，它有一个<code>compareTo(Object obj)</code>方法用于排序</li>
<li><code>Comparator</code>接口实际上是出自<code>java.util</code>包，它有一个<code>compare(Object obj1,Object obj2)</code>方法用于排序。</li>
</ul>
<h3 id="定制比较器">(1)定制比较器</h3>
<blockquote>
<p>所谓定制排序，是指不管数组元素本身是否已经实现了Comparable接口的compareTo方法，在排序时候都使用定制比较器的比较规则进行排序。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="comment">//...省略getter、setter和构造函数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        Person[] pers={</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"john"</span>,<span class="number">12</span>),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"lily"</span>,<span class="number">23</span>),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"lucy"</span>,<span class="number">5</span>),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"jack"</span>,<span class="number">18</span>),</span><br><span class="line">        };</span><br><span class="line">        List&lt;Person&gt; people = Arrays.asList(pers);</span><br><span class="line">        <span class="comment">//按照年龄排序</span></span><br><span class="line">        <span class="comment">// people.stream().sorted((o1, o2) -&gt; o1.getAge()-o2.getAge()).forEach(System.out::println);</span></span><br><span class="line">        people.stream().sorted(<span class="keyword">new</span> <span class="title class_">Comparator</span>&lt;Person&gt;() {</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">compare</span><span class="params">(Person o1, Person o2)</span> {</span><br><span class="line">                <span class="keyword">return</span> o1.getAge()-o2.getAge();</span><br><span class="line">            }</span><br><span class="line">        }).forEach(System.out::println);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722164653609.png" alt=""><figcaption>image-20230722164653609</figcaption>
</figure>
<blockquote>
<p>这里使用匿名内部类实现了<code>Comparator</code>接口。</p>
</blockquote>
<h3 id="实现compareto">(2)实现compareTo()</h3>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> <span class="keyword">implements</span> <span class="title class_">Comparable</span>&lt;Person&gt;{</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line">    <span class="keyword">private</span> Integer age;</span><br><span class="line">    <span class="comment">//...省略getter、setter和构造方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">compareTo</span><span class="params">(Person o)</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.getAge()-o.getAge();</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">       Person[] pers={</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"john"</span>,<span class="number">12</span>),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"lily"</span>,<span class="number">23</span>),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"lucy"</span>,<span class="number">5</span>),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"jack"</span>,<span class="number">18</span>),</span><br><span class="line">        };</span><br><span class="line">       <span class="comment">//按照年龄字段排序</span></span><br><span class="line">       Arrays.sort(pers);</span><br><span class="line">       Arrays.stream(pers).forEach(System.out::println);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722165831540.png" alt=""><figcaption>image-20230722165831540</figcaption>
</figure>
<h1 id="java集合">Java集合</h1>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/java-collection-hierarchy.png" style="zoom:80%;"></p>
<h2 id="常用的集合类">常用的集合类</h2>
<p>集合类存放于<code>java.util</code>包中，主要有3种：List、Set、Queue、Map</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708115413707.png" alt=""><figcaption>image-20230708115413707</figcaption>
</figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708115434822.png" alt=""><figcaption>image-20230708115434822</figcaption>
</figure>
<blockquote>
<p>注意：Collection接口不是Map的父接口</p>
</blockquote>
<h2 id="list">List</h2>
<p>List是有序的Collection,Java中List一共三个实现类：<code>ArrayList</code>、<code>Vector</code>和<code>LinkedList</code>。</p>
<h3 id="arraylist动态数组">ArrayList(动态数组)</h3>
<p><code>ArrayList</code><strong>是用数组实现的，允许对元素进行快速随机访问</strong>。ArrayList类在添加一个新元素时，如果现有数组的容量不够，则会将数组长度扩容为原来的1.5倍之后再添加。</p>
<blockquote>
<p>动态数组扩容并不是在原有连续的内存空间后进行简单的叠加，而是重新申请一块更大的新内存，并把现有容器中的元素逐个复制过去，然后销毁旧的内存。</p>
</blockquote>
<h3 id="linkedlist双向链表">LinkedList(双向链表)</h3>
<p><strong>LinkedList是顺序访问结构，内部使用双向链表实现</strong>。查询数据会消耗一定时间(需要遍历链表进行查询)。<strong>适合数据的动态插入和删除</strong>，随机访问和遍历速度较慢。可以当作堆、栈、队列和双向队列使用。</p>
<h3 id="vector动态数组实现线程安全">Vector(动态数组实现、线程安全)</h3>
<p><strong>Vector和<code>ArrayList</code>一样，也是通过动态数组实现，不同的是它是线程安全的，即某一时刻只有一个线程能够读写Vector，访问速度比<code>ArrayList</code>慢</strong>。</p>
<h2 id="set">Set</h2>
<p>Set最主要的特点是集合中的元素不能重复，存入Set的每个元素都必须定义<code>equals()</code>方法来确保对象的唯一性。</p>
<blockquote>
<p>对象的相等性本质是通过<code>hashCode</code>值(Java是根据对象的内存地址计算出的此序号)判断的，如果想要让两个不同对象视为相等的，就必须覆盖Object类的<code>hashCode</code>和<code>equals</code>方法。</p>
</blockquote>
<h3 id="hashset哈希表">HashSet(哈希表)</h3>
<p>基于哈希表实现，哈希表中存放的是哈希值。<code>HashSet</code>存储元素的顺序是按照哈希值来存的所以取数据也是按照哈希值取的，元素的哈希值是通过元素的<code>hashCode</code>方法来实现的。</p>
<blockquote>
<p><code>HashSet</code>和<code>LinkedHashSet</code>集合判断两个元素相等的标准是两个对象通过<code>hashCode</code>方法比较，并且两个对象的equals方法返回值也相等。</p>
<ul>
<li>HashSet不会有重复的元素</li>
<li>HashSet中最多只允许有一个<code>null</code></li>
<li>HashSet不是线程安全</li>
<li>HashSet不会维护数据插入的顺序，也不会对数据进行排序。</li>
</ul>
</blockquote>
<h3 id="linkedhashsethashsetlinkedhashmap">LinkedHashSet(HashSet+LinkedHashMap)</h3>
<p><code>LinkedHashSet</code>是<code>HashSet</code>的扩展，<code>HashSet</code>并不维护数据的顺序，而<code>LinkedHashSet</code>维护了数据插入的顺序。</p>
<h3 id="treeset红黑树自平衡的二叉排序树">TreeSet(红黑树，自平衡的二叉排序树)</h3>
<p>TreeSet基于二叉树对新添加的对象按照指定的顺序(升序、降序)，每添加一个对象都会进行排序，并将对象插入二叉树指定的位置。</p>
<blockquote>
<p>Integer和String等基础对象类型可以直接根据TreeSet的默认排序进行存储。</p>
<p>定制排序：如果自然排序不符合当前业务的需求，或者元素的类型没有实现<code>Comparable</code>接口，那么在创建TreeSet时，可以单独指定一个定制比较器<code>Comparator</code>的实现类对象，使用定制比较器的TreeSet判断两个元素相等的标准是通过<code>compare(Object o1,Object o2)</code>方法比较两个元素返回了<code>0</code>。</p>
</blockquote>
<h2 id="queue">Queue</h2>
<h3 id="queue与deque的区别">Queue与Deque的区别</h3>
<p><code>Queue</code> 是单端队列，只能从一端插入元素，另一端删除元素，实现上一般遵循 <strong>先进先出（FIFO）</strong> 规则。</p>
<p><code>Queue</code> 扩展了 <code>Collection</code> 的接口，根据<strong>因为容量问题而导致操作失败后处理方式的不同</strong>可以分为两类方法: 一种在操作失败后会抛出异常，另一种则会返回特殊值。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722172359653.png" alt=""><figcaption>image-20230722172359653</figcaption>
</figure>
<p><code>Deque</code>是双端队列，在队列的两端均可以插入或删除元素。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722172408436.png" alt=""><figcaption>image-20230722172408436</figcaption>
</figure>
<h3 id="priorityqueue优先级队列">PriorityQueue(优先级队列)</h3>
<ul>
<li><p><code>PriorityQueue</code>是在JDK1.5中被引入的, 其与<code>Queue</code>的区别在于元素出队顺序是与优先级相关的，即总是优先级最高的元素先出队。</p></li>
<li><p><code>PriorityQueue</code>利用了<strong>二叉堆</strong>的数据结构来实现的，底层使用可变长的数组来存储数据.</p></li>
<li><p><code>PriorityQueue</code>通过堆元素的上浮和下沉，实现了在<code>O(logn)</code>的时间复杂度内插入元素和删除堆顶元素。</p></li>
<li><p><code>PriorityQueue</code>是非线程安全的，且不支持存储<code>NULL</code>和<code>non-comparable</code>的对象。</p></li>
<li><p><code>PriorityQueue</code>默认是小顶堆，但可以接收一个<code>Comparator</code>作为构造参数，从而来自定义元素优先级的先后。</p></li>
</ul>
<h3 id="blockingqueue">BlockingQueue</h3>
<p>...</p>
<h2 id="map">Map</h2>
<p>Map是一种由多组key-value(键值对)集合在一起的结构，其中key是不能重复的。</p>
<blockquote>
<p>在Map中存储数据，实际上是将<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.464ex;" xmlns="http://www.w3.org/2000/svg" width="12.178ex" height="2.034ex" role="img" focusable="false" viewBox="0 -694 5382.6 899"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D458" d="M121 647Q121 657 125 670T137 683Q138 683 209 688T282 694Q294 694 294 686Q294 679 244 477Q194 279 194 272Q213 282 223 291Q247 309 292 354T362 415Q402 442 438 442Q468 442 485 423T503 369Q503 344 496 327T477 302T456 291T438 288Q418 288 406 299T394 328Q394 353 410 369T442 390L458 393Q446 405 434 405H430Q398 402 367 380T294 316T228 255Q230 254 243 252T267 246T293 238T320 224T342 206T359 180T365 147Q365 130 360 106T354 66Q354 26 381 26Q429 26 459 145Q461 153 479 153H483Q499 153 499 144Q499 139 496 130Q455 -11 378 -11Q333 -11 305 15T277 90Q277 108 280 121T283 145Q283 167 269 183T234 206T200 217T182 220H180Q168 178 159 139T145 81T136 44T129 20T122 7T111 -2Q98 -11 83 -11Q66 -11 57 -1T48 16Q48 26 85 176T158 471L195 616Q196 629 188 632T149 637H144Q134 637 131 637T124 640T121 647Z"></path></g><g data-mml-node="mi" transform="translate(521,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"></path></g><g data-mml-node="mi" transform="translate(987,0)"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(1754.8,0)"><path data-c="2192" d="M56 237T56 250T70 270H835Q719 357 692 493Q692 494 692 496T691 499Q691 511 708 511H711Q720 511 723 510T729 506T732 497T735 481T743 456Q765 389 816 336T935 261Q944 258 944 250Q944 244 939 241T915 231T877 212Q836 186 806 152T761 85T740 35T732 4Q730 -6 727 -8T711 -11Q691 -11 691 0Q691 7 696 25Q728 151 835 230H70Q56 237 56 250Z"></path></g><g data-mml-node="mi" transform="translate(3032.6,0)"><path data-c="1D463" d="M173 380Q173 405 154 405Q130 405 104 376T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Q21 294 29 316T53 368T97 419T160 441Q202 441 225 417T249 361Q249 344 246 335Q246 329 231 291T200 202T182 113Q182 86 187 69Q200 26 250 26Q287 26 319 60T369 139T398 222T409 277Q409 300 401 317T383 343T365 361T357 383Q357 405 376 424T417 443Q436 443 451 425T467 367Q467 340 455 284T418 159T347 40T241 -11Q177 -11 139 22Q102 54 102 117Q102 148 110 181T151 298Q173 362 173 380Z"></path></g><g data-mml-node="mi" transform="translate(3517.6,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(4046.6,0)"><path data-c="1D459" d="M117 59Q117 26 142 26Q179 26 205 131Q211 151 215 152Q217 153 225 153H229Q238 153 241 153T246 151T248 144Q247 138 245 128T234 90T214 43T183 6T137 -11Q101 -11 70 11T38 85Q38 97 39 102L104 360Q167 615 167 623Q167 626 166 628T162 632T157 634T149 635T141 636T132 637T122 637Q112 637 109 637T101 638T95 641T94 647Q94 649 96 661Q101 680 107 682T179 688Q194 689 213 690T243 693T254 694Q266 694 266 686Q266 675 193 386T118 83Q118 81 118 75T117 65V59Z"></path></g><g data-mml-node="mi" transform="translate(4344.6,0)"><path data-c="1D462" d="M21 287Q21 295 30 318T55 370T99 420T158 442Q204 442 227 417T250 358Q250 340 216 246T182 105Q182 62 196 45T238 27T291 44T328 78L339 95Q341 99 377 247Q407 367 413 387T427 416Q444 431 463 431Q480 431 488 421T496 402L420 84Q419 79 419 68Q419 43 426 35T447 26Q469 29 482 57T512 145Q514 153 532 153Q551 153 551 144Q550 139 549 130T540 98T523 55T498 17T462 -8Q454 -10 438 -10Q372 -10 347 46Q345 45 336 36T318 21T296 6T267 -6T233 -11Q189 -11 155 7Q103 38 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(4916.6,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"></path></g></g></g></svg></mjx-container></span>的数据存储在<code>Map.Entry</code>接口的实例中，再向Map集合中插入<code>Map.Entry</code>的实例化对象。</p>
</blockquote>
<h3 id="hashmap数组链表红黑树">HashMap(数组+链表+红黑树)</h3>
<p>HashMap根据键的hashCode值存放数据，大多数情况下可以直接定位到它的值，因而具有很快的访问速度，但遍历顺序却是不确定的。</p>
<blockquote>
<p>HashMap最多只允许一条记录的键为null，允许多条记录的值为null</p>
<p>HashMap非线程安全，可以使用<code>Collections</code>的<code>synchronizedMap</code>方法使HashMap具有线程安全的能力</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">HashMap&lt;Object, Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">Map&lt;Object, Object&gt; synchronizedMap = Collections.synchronizedMap(map);</span><br></pre></td></tr></table></figure>
<p>或者使用<code>ConcurrentHashMap</code>。</p>
</blockquote>
<p>Jdk7及之前的HashMap结构：<strong>数组+链表</strong></p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708164303190.png" alt=""><figcaption>image-20230708164303190</figcaption>
</figure>
<p>JDK1.8的HashMap结构：<strong>数据+链表+红黑树</strong></p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708164340649.png" alt=""><figcaption>image-20230708164340649</figcaption>
</figure>
<p>JDK1.8之后在解决哈希冲突时有了较大的变化，当链表长度大于阈值(默认为8)(<strong>将链表转换成红黑树前会判断，如果当前数组的长度小于64，那么会选择进行数组扩容，而不是转换为红黑树</strong>)时，将链表转化为红黑树，以减少搜索时间。</p>
<blockquote>
<p>红黑树就是为了解决二叉搜索树的缺陷，因为二叉搜索树在某些情况下会退化成一个线性结构。</p>
</blockquote>
<h4 id="hashmap为什么线程不安全">HashMap为什么线程不安全？</h4>
<p>可能会发生数据覆盖。</p>
<ul>
<li>两个线程1，2同时进行<code>put</code>操作，并且法神了哈希冲突(hash函数计算出的插入下标是相同的)</li>
<li>不同的线程可能在不同的时间片获得CPU执行的机会，当前线程1执行完哈希冲突后，由于时间片耗尽挂起。线程2先完成了插入操作。</li>
<li>随后，线程1获得时间片，由于之前已经进行过hash碰撞的判断，所以此时会直接进行插入，这就导致线程2插入的数据被线程1覆盖了。</li>
</ul>
<h3 id="linkedhashmap记录插入顺序">LinkedHashMap(记录插入顺序)</h3>
<p>LinkedHashMap是HashMap的子类，它维护了一个双向链表，此链表定义了迭代顺序，此迭代顺序通常就是将键插入映射中的顺序。</p>
<h3 id="hashtable线程安全">HashTable(线程安全)</h3>
<p>HashMap和HashTable都是哈希表，二者区别如下：</p>
<table>
<thead>
<tr class="header">
<th style="text-align: center;">表</th>
<th style="text-align: center;">底层结构</th>
<th style="text-align: center;">线程安全(同步)</th>
<th style="text-align: center;">版本</th>
<th style="text-align: center;">效率</th>
<th style="text-align: center;">key,value是否允许为null</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">HashMap</td>
<td style="text-align: center;">哈希表</td>
<td style="text-align: center;">不安全(不同步)</td>
<td style="text-align: center;">较新</td>
<td style="text-align: center;">较高</td>
<td style="text-align: center;">允许</td>
</tr>
<tr class="even">
<td style="text-align: center;">HashTable</td>
<td style="text-align: center;">哈希表</td>
<td style="text-align: center;">安全(同步)</td>
<td style="text-align: center;">较老</td>
<td style="text-align: center;">较低下</td>
<td style="text-align: center;">不允许</td>
</tr>
</tbody>
</table>
<h3 id="treemap可排序">TreeMap(可排序)</h3>
<p>TreeMap的集合是基于<strong>红黑树</strong>(Red-Black Tree)的可导航<code>NavigableMap</code>实现的。TreeMap中的映射关系要么根据其key键的自然顺序进行排序，要么根据创建TreeMap对象时提供给key键的定制排序<code>Comparator</code>接口实现类进行排序，具体取决于使用的构造方法。</p>
<h3 id="concurrenthashmap线程安全">ConcurrentHashMap(线程安全)</h3>
<p><code>ConcurrentHashMap</code>是HashMap中支持高并发、高吞吐量的线程安全的版本。它由<code>Segment</code>数组结构和<code>HashEntry</code>数组结构组成。Segment在ConcurrentHashMap中扮演锁的角色，HashEntry则用于存储键值对数据。</p>
<p><strong>JDK1.7中ConcurrentHashMap实现</strong>：JDK1.7中ConcurrentHashMap采用分段锁的思想实现并发操作，因此是线程安全的。ConcurrentHashMap有多个Segment组成(Segment的数量也是锁的并发度)，每个Segment均继承于ReentrantLock并单独加锁，所以每次进行加锁操作时锁住的都是一个Segment，这样只要保证每个Segment都是线程安全的，也就实现了全局的线程安全。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708174345789.png" alt="image-20230708174345789" style="zoom: 67%;"></p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230814163938377.png" alt="image-20230814163938377"></p>
<p><strong>JDK1.8中ConcurrentHashMap的实现</strong>：JDK1.8中ConcurrentHashMap弃用了Segment分段锁，改用<code>Synchronized+CAS</code>实现对多线程的安全操作。同时，JDK1.8在ConcurrentHashMap中引入了红黑树。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230708174317589.png" alt="image-20230708174317589" style="zoom:80%;"></p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230814163425044.png" alt=""><figcaption>ConcurrentHashMap结构</figcaption>
</figure>
<p>Java8中，锁粒度更细，<code>synchronized</code>只锁定当前链表或红黑二叉树的首节点，这样只要hash不冲突，就不会产生并发，就不会影响到其他Node的读写，效率大幅提升。</p>
<h2 id="listsetmap三者的区别">List、Set、Map三者的区别？</h2>
<ul>
<li><code>List</code>:有序集合(有序指存入的顺序和取出的顺序相同，不是按照元素的某些特性排序)，可存储重复元素，可存储多个null。(<code>LinkedList、ArrayList、Vector</code>)</li>
<li><code>Set</code>:无序集合(元素存入和取出顺序不一定相同)，集合中的元素不能重复，只能存储一个null。(<code>HashSet、TreeSet</code>)</li>
<li><code>Map</code>:使用键值对的方式对元素进行存储，<code>key</code>是唯一的，不能重复，value可以重复。(<code>HashMap、TreeMap、LinkedHashMap、WeakHashMap、IdentityHashMap</code>)</li>
</ul>
<h2 id="常用集合框架的底层实现">常用集合框架的底层实现</h2>
<ul>
<li><p>List:</p>
<p><code>ArrayList</code>:数组</p>
<p><code>LinkedList</code>:双向链表</p></li>
<li><p>Set:</p>
<p><code>HashSet</code>:底层基于<code>HashMap</code>实现，<code>HashSet</code>存入读取元素的方式和<code>HashMap</code>中的key是一致的。</p>
<p><code>TreeSet</code>:红黑树</p></li>
<li><p>Map:</p>
<p><code>HashMap</code>:JDK1.8之前<code>HashMap</code>由<strong>数组+链表</strong>组成，<code>JDK1.8</code>之后由<strong>数组+链表/红黑树</strong>组成，当链表长度大于8时，链表转化为红黑树。这样做的目的是能提高<code>HashMap</code>的性能，因为红黑树的查找元素的时间复杂度远小于链表。</p>
<p><code>HashTable</code>:数组+链表</p>
<p><code>TreeMap</code>:红黑树</p></li>
</ul>
<h1 id="java并发编程">Java并发编程</h1>
<h2 id="多线程基础知识">多线程基础知识</h2>
<h3 id="进程和线程的区别">进程和线程的区别？</h3>
<p>进程是操作系统资源分配、调度和管理的最小单位。进程是处于运行过程的程序。</p>
<p>线程是CPU执行和调度的最小单元。</p>
<p>一个程序运行至少有一个进程，一个进程中可以包含多个线程，但至少要包含一个线程。</p>
<p><strong>多个线程可以共享进程中堆和方法区的资源，但是每个线程又可以有自己的程序计数器、虚拟机栈和本地方法栈。</strong></p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722173556712.png" alt="image-20230722173556712" style="zoom: 80%;"></p>
<h3 id="堆和方法区">堆和方法区</h3>
<p>堆和方法区是所有线程共享的资源，其中堆是进程中最大的一块内存，主要用于存放新创建的对象 (几乎所有对象都在这里分配内存)，方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。</p>
<h3 id="并行和并发的区别">并行和并发的区别？</h3>
<p><strong>并行</strong>是指在同一时刻，有多条指令在多个处理器上同时执行。</p>
<p><strong>并发</strong>是指在同一时刻只能有一条指令执行，但多个进程的指令被快速轮换执行，使得在宏观上具有多个进程同时执行的效果。</p>
<h3 id="同步和异步">同步和异步</h3>
<ul>
<li><strong>同步</strong>：发出一个调用之后，在没有得到结果之前，该调用就不可以返回，一直等待。</li>
<li><strong>异步</strong>：调用在发出之后，不用等待返回结果，该调用直接返回。</li>
</ul>
<h3 id="线程安全和线程不安全">线程安全和线程不安全</h3>
<p>线程安全和不安全是在多线程环境下对于同一份数据的访问是否能够保证其正确性和一致性的描述。</p>
<ul>
<li>线程安全指的是在多线程环境下，对于同一份数据，不管有多少个线程同时访问，都能保证这份数据的正确性和一致性。</li>
<li>线程不安全则表示在多线程环境下，对于同一份数据，多个线程同时访问时可能会导致数据混乱、错误或者丢失。</li>
</ul>
<h3 id="线程的生命周期和状态">线程的生命周期和状态</h3>
<blockquote>
<p>线程状态划分并不唯一，下面参考《Java并发编程的艺术》</p>
</blockquote>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722174926942.png" alt=""><figcaption>image-20230722174926942</figcaption>
</figure>
<blockquote>
<p>线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。线程转化过程如下：</p>
</blockquote>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230722175030601.png" alt=""><figcaption>image-20230722175030601</figcaption>
</figure>
<h3 id="死锁">死锁</h3>
<p>死锁指的是两个或两个以上的进程在执行过程中，因争夺资源而造成的一种互相等待的现象，若无外力作用，它们都将无法推进下去。</p>
<blockquote>
<figure>
<img data-src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-4/2019-4%E6%AD%BB%E9%94%811.png" alt=""><figcaption>线程死锁示意图</figcaption>
</figure>
<p>线程A持有资源2，线程B持有资源1，他们同时都想申请对方的资源，所以这两个线程就会互相等待而进入死锁状态。</p>
</blockquote>
<p>产生死锁：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DeadLockDemo</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Object resource1=<span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Object resource2=<span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;{</span><br><span class="line">            <span class="keyword">synchronized</span> (resource1){</span><br><span class="line">                System.out.println(Thread.currentThread()+<span class="string">"get resource1"</span>);</span><br><span class="line">                <span class="keyword">try</span> {</span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">                } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                }</span><br><span class="line">                System.out.println(<span class="string">"waiting get resource2"</span>);</span><br><span class="line">                <span class="keyword">synchronized</span> (resource2){</span><br><span class="line">                    System.out.println(Thread.currentThread()+<span class="string">"get resource2"</span>);</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">        }).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;{</span><br><span class="line">            <span class="keyword">synchronized</span> (resource2){</span><br><span class="line">                System.out.println(Thread.currentThread()+<span class="string">"get resource2"</span>);</span><br><span class="line">                <span class="keyword">try</span> {</span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">                } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                }</span><br><span class="line">                System.out.println(<span class="string">"waiting get resource1"</span>);</span><br><span class="line">                <span class="keyword">synchronized</span> (resource1){</span><br><span class="line">                    System.out.println(Thread.currentThread()+<span class="string">"get resource1"</span>);</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">        }).start();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230716220638662.png" alt=""><figcaption>image-20230716220638662</figcaption>
</figure>
<p>上面代码产生死锁的主要原因是线程1获取到了资源1，线程2获取到了资源2，线程1继续获取资源2而产生阻塞，线程2继续获取资源1而产生阻塞。</p>
<p>解决该问题最简单的方式就是两个线程按顺序获取资源，线程1和线程2都先获取资源1在获取资源2，无论哪个线程先获取到资源1，另一个线程都会因无法获取线程1而阻塞，等到先获取线程1的线程释放资源1，另一个线程获取资源1，这样两个线程可以轮流获取资源1和资源2，代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Object resource1=<span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Object resource2=<span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;{</span><br><span class="line">        <span class="keyword">synchronized</span> (resource1){</span><br><span class="line">            System.out.println(Thread.currentThread()+<span class="string">"get resource1"</span>);</span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">            } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            }</span><br><span class="line">            System.out.println(<span class="string">"waiting get resource2"</span>);</span><br><span class="line">            <span class="keyword">synchronized</span> (resource2){</span><br><span class="line">                System.out.println(Thread.currentThread()+<span class="string">"get resource2"</span>);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }).start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;{</span><br><span class="line">        <span class="keyword">synchronized</span> (resource1){</span><br><span class="line">            System.out.println(Thread.currentThread()+<span class="string">"get resource1"</span>);</span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">            } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            }</span><br><span class="line">            System.out.println(<span class="string">"waiting get resource2"</span>);</span><br><span class="line">            <span class="keyword">synchronized</span> (resource2){</span><br><span class="line">                System.out.println(Thread.currentThread()+<span class="string">"get resource2"</span>);</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }).start();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="java中守护线程和用户线程">Java中守护线程和用户线程</h3>
<p>守护线程是指在程序运行的时候在后台提供一种通用服务的线程，这种线程并不属于程序中不可或缺的部分。</p>
<blockquote>
<p>通俗的讲，任何一个守护线程都是整个JVM中所有非守护线程的“保姆”。</p>
</blockquote>
<p>守护线程的一个典型例子就是垃圾回收器，只要JVM启动,它始终在运行，实时监控和管理系统中可以被回收的资源。</p>
<blockquote>
<p>将一个用户线程设置为守护线程的方法就是在调用start启动线程之前调用对象的<code>setDaemon(true)</code>方法，如果将以上参数设置为false,则标识的是用户线程模式。</p>
</blockquote>
<h2 id="常见的java线程的创建方式给出代码示例">常见的Java线程的创建方式(给出代码示例)</h2>
<ul>
<li>继承<code>Thread</code>类</li>
<li>实现<code>Runnable</code>接口</li>
<li>通过<code>ExecutorService</code>和<code>Callable&lt;Class&gt;</code>实现有返回值的线程</li>
<li>基于<strong>线程池</strong></li>
</ul>
<h2 id="线程池">线程池</h2>
<h3 id="为什么用线程池介绍下线程池的几个参数">为什么用线程池？介绍下线程池的几个参数</h3>
<p>线程池的主要作用是线程复用、线程资源管理、控制操作系统的最大并发数，以保证系统高效(通过线程资源复用)和安全(通过控制最大并发线程数)的运行。</p>
<p>使用线程池可以降低资源消耗、提高响应速度、提高线程的可管理性，线程池<code>ThreadPoolExecutor</code>参数如下：</p>
<ul>
<li><code>corePoolSize</code>：线程池中核心线程数:任务队列未达到队列容量时，最大可以同时运行的线程数量。</li>
<li><code>maximumPoolSize</code>：线程池中最大线程数。</li>
<li><code>keepAliveTime</code>:当前线程数量大于核心线程数时，空闲线程的等待时间。</li>
<li><code>unit</code>:keepAliveTime的时间单位</li>
<li><code>workQueue</code>:任务队列，被提交但尚未被执行的任务存放的地方。</li>
<li><code>threadFactory</code>:线程工厂，用于创建线程，可使用默认的线程工厂或自定义线程工厂。</li>
<li><code>handler</code>:拒绝策略：由于任务过多或其他原因导致线程池无法处理时的任务拒绝策略。</li>
</ul>
<h3 id="创建线程池的方法">创建线程池的方法</h3>
<p>创建线程池的方法：通过<strong>Executors工厂方法创建</strong>和通过<strong>new ThreadPoolExecutor方法</strong>创建</p>
<ul>
<li><p><code>Executros</code>工厂方法创建，在工具类Execurots提供了一些静态的工厂方法</p>
<p><code>newFixedThreadPool</code>:创建固定大小的线程池。</p>
<p><code>newCachedThreadPool</code>:创建一个带缓冲的线程池。</p>
<p><code>newSingleThreadExecutor</code>:创建一个单线程的线程池</p>
<p><code>newScheduledThreadPool</code>:调度线程池，可以按照一定的周期执行任务，即定时任务.</p></li>
<li><p><code>new ThreadPoolExecutor</code>创建：</p></li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="type">int</span> corePoolSize,				<span class="comment">//核心线程数</span></span><br><span class="line">                       <span class="type">int</span> maximumPoolSize,		<span class="comment">//最大线程数</span></span><br><span class="line">                       <span class="type">long</span> keepAliveTime,		</span><br><span class="line">                       TimeUnit unit,</span><br><span class="line">                       BlockingQueue&lt;Runnable&gt; workQueue,	<span class="comment">//存放任务的阻塞队列</span></span><br><span class="line">                       ThreadFactory threadFactory,		<span class="comment">//为线程池提供创建新线程的线程工厂</span></span><br><span class="line">                       RejectedExecutionHandler handler)<span class="comment">//拒绝策略</span></span><br></pre></td></tr></table></figure>
<h2 id="线程通信方式待补充">线程通信方式(待补充)</h2>
<ul>
<li><strong>共享变量</strong>：多个线程通过共享变量来进行通信。线程可以通过读写共享变量来传递数据。对于共享变量的访问需要加上同步机制，如<strong>synchronized</strong>关键字或<strong>Lock</strong>接口，以确保线程安全。</li>
<li><strong>等待通知机制</strong>：可以使用wait()和notify()或者wait()和notifyAll()方法实现线程间的等待和唤醒操作，以便在特定条件下进行通信。</li>
<li><strong>互斥锁</strong>：互斥锁用于保护共享资源，确保只有一个线程可以访问该资源，Java中可以使用<strong>ReentrantLock</strong>类来实现互斥锁。</li>
<li><strong>管道输入/输出流</strong>：Java提供了PipedInputStream和PipedOutputStream类，可以在两个相关联的线程之间进行数据传递。一个线程将数据写入PipedOutputStream,另一个线程则从相应的PipedInputStream中读取数据。</li>
<li><strong>条件变量(Condition)</strong>:条件变量用于在线程之间传递信息，以及在特定条件下进行等待和唤醒。Java中，<code>ReentrantLock</code>类配合<code>Condition</code>接口可以实现条件变量。</li>
<li><strong>并发集合</strong>：Java中提供了多种线程安全的集合类，如ConcurrentHashMap、ConcurrentLinkedQueue等，它们可以在多线程环境下安全地操作数据。</li>
</ul>
<h2 id="synchronized关键字">Synchronized关键字</h2>
<p>在多线程的环境下，多个线程同时访问共享资源会出现一些问题，而synchronized关键字是用来保证线程同步的，可以保证它修饰的方法或者代码块在任意时刻只能有一个线程执行。</p>
<p>synchronized关键字的三大特性如下：</p>
<ul>
<li>原子性：一个或多个操作要么全部执行成功，要么全部执行失败，synchronized关键字可以保证只有一个线程拿到锁，访问共享资源。</li>
<li>可见性：当一个线程对共享变量进行修改后，其他线程可以立刻看到。执行synchronized时，会对应执行lock、unlock原子操作，保证可见性。</li>
<li>有序性：程序的执行顺序会按照代码的先后顺序执行。</li>
</ul>
<blockquote>
<p>synchronized可以保证并发编程的三大特性：原子性、可见性、有序性，而volatile关键字只能保证可见性和有序性。</p>
</blockquote>
<h2 id="threadlocal用过吗它是怎么解决线程安全的结构了解吗">ThreadLocal用过吗？它是怎么解决线程安全的？结构了解吗？</h2>
<p>通常情况下，我们创建的变量可以被任何一个线程访问并修改。如果想实现每一个线程都有自己的专属本地变量该如何解决？JDK自带的<code>ThreadLocal</code>正是为了解决这个问题。</p>
<p><code>ThreadLocal</code>类主要解决的是让每个线程绑定自己的值，可以将ThreadLocal类形象的比喻成存放数据的盒子，盒子中可以存储每个线程的私有数据。</p>
<p>如果你创建了一个<code>ThreadLocal</code>变量，那么访问这个变量的每个线程都会有这个变量的本地副本，它们可以使用<code>get()</code>和<code>set()</code>方法来获取默认值或将其值更改为当前线程所存的副本的值，从而避免了线程安全问题。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808111612153.png" alt=""><figcaption>image-20230808111612153</figcaption>
</figure>
<p>ThreadLocal类的set方法如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(T value)</span> {</span><br><span class="line">    <span class="comment">//获取当前请求的线程</span></span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">    <span class="comment">//取出 Thread 类内部的 threadLocals 变量(哈希表结构)</span></span><br><span class="line">    <span class="type">ThreadLocalMap</span> <span class="variable">map</span> <span class="operator">=</span> getMap(t);</span><br><span class="line">    <span class="keyword">if</span> (map != <span class="literal">null</span>)</span><br><span class="line">        <span class="comment">// 将需要存储的值放入到这个哈希表中</span></span><br><span class="line">        map.set(<span class="built_in">this</span>, value);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        createMap(t, value);</span><br><span class="line">}</span><br><span class="line">ThreadLocalMap <span class="title function_">getMap</span><span class="params">(Thread t)</span> {</span><br><span class="line">    <span class="keyword">return</span> t.threadLocals;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>通过上面这些内容，<strong>最终的变量是放在了当前线程的<code>ThreadLocalMap</code>中，并不是存在<code>ThreadLocal</code>上，<code>ThreadLocal</code>可以理解为只是<code>ThreadLocalMap</code>的封装，传递了变量值</strong>。<code>ThreadLocal</code>类中可以通过<code>Thread.currentThread()</code>获取到当前线程对象后，直接通过<code>getMap(Thread t)</code>可以访问到该线程的<code>ThreadLocalMap</code>对象。</p>
<p><strong>每个<code>Thread</code>中都具有一个<code>ThreadLocalMap</code>，而<code>ThreadLocalMao</code>可以存储以<code>ThreadLocal</code>为Key,Object对象为value的键值对。</strong></p>
<blockquote>
<p>比如我们在同一个线程中声明了两个<code>ThreadLocal</code>对象的话，<code>Thread</code>内部都是使用仅有的那个<code>ThreadLocalMap</code>存放数据的，<code>ThreadLocalMap</code>的key就是<code>ThreadLocal</code>对象，value就是<code>ThreadLocal</code>对象调用<code>set</code>方法设置的值。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadLocalExample</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> {</span><br><span class="line">    <span class="comment">// SimpleDateFormat 不是线程安全的，所以每个线程都要有自己独立的副本</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ThreadLocal&lt;SimpleDateFormat&gt; formatter =</span><br><span class="line">            ThreadLocal.withInitial(() -&gt; <span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>(<span class="string">"yyyyMMdd HHmm"</span>));</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Thread Name="</span> + Thread.currentThread().getName() + <span class="string">"default formatter="</span> + formatter.get().toPattern());</span><br><span class="line">        <span class="keyword">try</span> {</span><br><span class="line">            Thread.sleep(<span class="keyword">new</span> <span class="title class_">Random</span>().nextInt(<span class="number">1000</span>));</span><br><span class="line">        } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        }</span><br><span class="line">        formatter.set(<span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>());</span><br><span class="line">        System.out.println(<span class="string">"Thread Name="</span> + Thread.currentThread().getName() + <span class="string">"formatter="</span> + formatter.get().toPattern());</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException {</span><br><span class="line"></span><br><span class="line">        <span class="type">ThreadLocalExample</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadLocalExample</span>();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) {</span><br><span class="line">            <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(obj, <span class="string">""</span> + i);</span><br><span class="line">            Thread.sleep(<span class="keyword">new</span> <span class="title class_">Random</span>().nextInt(<span class="number">1000</span>));</span><br><span class="line">            t.start();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815113635791.png" alt=""><figcaption>image-20230815113635791</figcaption>
</figure>
<p>从输出中可以看出，虽然<code>Thread-0</code>已经改变了<code>formatter</code>的值，但<code>Thread-1</code>默认格式化值与初始化值相同，其他线程也一样。</p>
<h2 id="reentrantlock可重入锁">ReentrantLock(可重入锁)</h2>
<h3 id="reentrantlock了解吗是公平锁吗">ReentrantLock了解吗？是公平锁吗？</h3>
<p><strong>ReentrantLock(可重入锁)</strong>实现了Lock接口,是一个<strong>可重入且独占式</strong>的锁，和<code>synchronized</code>关键字类似，不过ReentrantLock更灵活、强大，增加了轮询、超时、中断、公平锁和非公平锁等高级功能。</p>
<blockquote>
<p><strong>重入锁</strong>，是指在同一线程中，外部方法获得锁之后，内层递归方法依然可以获得该锁，如果锁不具备重入性，那么当同一个线程两次获取锁的时候就会发生死锁。</p>
</blockquote>
<p><code>ReentrantLock</code>默认使用非公平锁，也可以通过构造器显示指定非公平锁。</p>
<ul>
<li><strong>公平锁</strong>：锁被释放之后，先申请的线程先得到锁。性能较差，公平锁为了保证时间上的绝对顺序，上下文切换更频繁。</li>
<li><strong>非公平锁</strong>：锁被释放之后，后申请的线程可能会先获取到锁，是随机或者按照其他优先级排序的。性能更好，单可能会导致某些线程无法获取到锁。</li>
</ul>
<h3 id="synchronized与reentrantlock的异同"><strong>synchronized与ReentrantLock的异同</strong></h3>
<ul>
<li><p><strong>两者都是可重入锁</strong></p></li>
<li><p><code>synchronized</code>依赖于<code>JVM</code>而<code>ReentrantLock</code>依赖于API,<code>synchronized</code>是依赖于JVM的，而<code>ReentrantLock</code>是JDK层面实现的也就是API层面，需要<code>lock()</code>和<code>unlock()</code>方法配合<code>try/finally</code>语句块来完成。</p></li>
<li><p><code>synchronized</code>不需要用户手动释放锁，<code>ReentrantLock</code>则需要用户手动释放锁。</p></li>
<li><p><code>ReentrantLock</code>比<code>synchronized</code>增加了一些高级功能：</p>
<p><strong>等待可中断</strong> : <code>ReentrantLock</code>提供了一种能够中断等待锁的线程的机制，通过 <code>lock.lockInterruptibly()</code> 来实现这个机制。也就是说正在等待的线程可以选择放弃等待，改为处理其他事情。</p>
<p><strong>可实现公平锁</strong> : <code>ReentrantLock</code>可以指定是公平锁还是非公平锁。而<code>synchronized</code>只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。<code>ReentrantLock</code>默认情况是非公平的，可以通过 <code>ReentrantLock</code>类的<code>ReentrantLock(boolean fair)</code>构造方法来制定是否是公平的。</p>
<p><strong>可实现选择性通知（锁可以绑定多个条件）</strong>: <code>synchronized</code>关键字与<code>wait()</code>和<code>notify()</code>/<code>notifyAll()</code>方法相结合可以实现等待/通知机制。<code>ReentrantLock</code>类当然也可以实现，但是需要借助于<code>Condition</code>接口与<code>newCondition()</code>方法。</p></li>
</ul>
<h2 id="java并发关键字">Java并发关键字</h2>
<h3 id="volatile">volatile</h3>
<p>Java除了使用synchronized保证变量的同步，还是用了稍弱的同步机制，即<code>volatile</code>。<code>volatile</code>也用于确保将变量的更新操作通知到其他线程。</p>
<p>使用<code>volatile</code>关键字修饰的变量叫做<code>volatile</code>变量，<code>volatile</code>具备两种特性:</p>
<ul>
<li><strong>保证变量对所有线程可见</strong>，在一个线程修改了变量的值后，新的值对于其他线程是可以立即获取的。</li>
<li><strong>禁止指令重排</strong>，即volatile变量不会被缓存在寄存器种或者对其他处理器不可见的地方，因此在读取volatile变量时总会返回最新写入的值。</li>
</ul>
<p>在访问volatile变量时不会进行加锁操作，也就不会执行线程阻塞，因此volatile是一种比synchronized更轻量级的同步机制，主要适用于一个变量被多个线程共享，多个线程均可针对这个变量执行赋值或者读取操作。</p>
<p><strong><code>volatile</code>关键字能保证变量的可见性，但不能保证对变量的操作是原子性的。</strong></p>
<p>注意：<strong>volatile可以严格保证变量的单次读、写操作的原子性，并不能保证像<code>i++</code>这种操作的原子性，因为<code>i++</code>在本质上是读、写两次操作</strong>。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230810165036137.png" alt=""><figcaption>image-20230810165036137</figcaption>
</figure>
<h3 id="countdownlatch倒计时器">CountDownLatch(倒计时器)</h3>
<p>CountDownLatch位于<code>java.util.concurrent</code>包下，是一个同步工具类，<strong>基于线程计数器来实现并发访问控制，允许一个或多个线程一起等待其他线程的操作执行完毕后再执行相关操作</strong>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CountDownLatchTest</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException {</span><br><span class="line">        <span class="keyword">final</span> <span class="type">CountDownLatch</span> <span class="variable">latch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">2</span>);</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;{</span><br><span class="line">            System.out.println(<span class="string">"子线程1正在执行"</span>);</span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">            } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">            }</span><br><span class="line">            System.out.println(<span class="string">"子线程1执行完毕"</span>);</span><br><span class="line">            latch.countDown();<span class="comment">//子线程1执行完毕后调用countDown方法</span></span><br><span class="line">        }).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;{</span><br><span class="line">            System.out.println(<span class="string">"子线程2正在执行"</span>);</span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">            } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">            }</span><br><span class="line">            System.out.println(<span class="string">"子线程2执行完毕"</span>);</span><br><span class="line">            latch.countDown();<span class="comment">//子线程2执行完毕后调用countDown方法</span></span><br><span class="line">        }).start();</span><br><span class="line">        System.out.println(<span class="string">"等待两个子线程执行完毕..."</span>);</span><br><span class="line">        <span class="comment">//在CountDownLatch上等待子线程执行完毕</span></span><br><span class="line">        latch.await();</span><br><span class="line">        System.out.println(<span class="string">"两个子线程都已经执行完毕，继续执行主线程"</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码先定义了一个计数器个数为2的CountDownLatch,然后定义了两个子线程并启动该子线程，子线程在执行完业务代码后执行latch.countDown()减少一个信号量，表示自己已经执行完毕。主线程调用<code>latch.await()</code>阻塞等待，在所有子线程都执行完毕并调用了countDown函数时，表示所有线程均执行完毕，这时程序会主动唤醒主线程并接着执行主线程的业务逻辑。</p>
<h3 id="cyclicbarrier循环栅栏">CyclicBarrier(循环栅栏)</h3>
<p><strong>CyclicBarrier(循环栅栏)是一个同步工具，可以实现让一组线程等待至某状态之后再全部同时执行</strong>。在所有等待线程都被释放之后，CyclicBarrier可被重用。CyclicBarrier的运行状态叫做<code>Barrier</code>状态，在调用<code>await</code>方法后，线程就处于<code>Barrier</code>状态。</p>
<p>CyclicBarrier中await方法的两种实现：</p>
<ul>
<li><code>public int await()</code>:挂起当前线程，直到所有线程都为<code>Barrier</code>状态再同时执行后续的任务。</li>
<li><code>public int await(long timeout,TimeUnit unit)</code>:设置一个超时时间，在超时时间过后，如果还有线程未达到Barrier状态，则不再等待，让达到Barrier状态的线程继续之后后续的任务。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CyclicBarrierTest</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> threadCount=<span class="number">10</span>;<span class="comment">//请求的数量</span></span><br><span class="line">    <span class="comment">//需要同步的线程数量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> CyclicBarrier cyclicBarrier=<span class="keyword">new</span> <span class="title class_">CyclicBarrier</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException {</span><br><span class="line">        <span class="comment">//创建线程池</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">10</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; threadCount; i++) {</span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> threadNum=i;</span><br><span class="line">            Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">            threadPool.execute(()-&gt;{</span><br><span class="line">                System.out.println(<span class="string">"threadNum:"</span>+threadNum+<span class="string">" is ready"</span>);</span><br><span class="line">                <span class="keyword">try</span> {</span><br><span class="line">                    <span class="comment">//等待其他线程也成为Barrier状态,超时时间设置30秒</span></span><br><span class="line">                    cyclicBarrier.await(<span class="number">30</span>, TimeUnit.SECONDS);</span><br><span class="line">                } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line">                    System.out.println(<span class="string">"CyclicBarrierException"</span>);</span><br><span class="line">                }</span><br><span class="line">                System.out.println(<span class="string">"threadNum:"</span>+threadNum+<span class="string">" is finished"</span>);</span><br><span class="line">            });</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230811113606308.png" alt=""><figcaption>image-20230811113606308</figcaption>
</figure>
<h3 id="semaphore信号量">Semaphore(信号量)</h3>
<p><code>synchronized</code> 和 <code>ReentrantLock</code>都是一次只允许一个线程访问某个资源，而<code>Semaphore</code>(信号量)可以用来控制同时访问特定资源的线程数量。</p>
<p><strong>Semaphore指信号量，用于控制同时访问某些资源的线程个数</strong>，具体做法为通过调用<code>acquire()</code>获得一个许可，以便其他线程继续使用。</p>
<p>Semaphore常用于多个线程共享有限资源的情况下，比如办公室有2台打印机，但是有5个员工需要使用，一台打印机同时只能被一个员工使用，其他员工排队等候，且只有该打印机使用完毕被“释放”后其他员工方可使用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SemaphoreTest</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">int</span> printNumber=<span class="number">5</span>;<span class="comment">//设置线程数，即员工数量</span></span><br><span class="line">        <span class="type">Semaphore</span> <span class="variable">semaphore</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Semaphore</span>(<span class="number">2</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; printNumber; i++) {</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Worker</span>(i,semaphore).start();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Worker</span> <span class="keyword">extends</span> <span class="title class_">Thread</span>{</span><br><span class="line">        <span class="keyword">private</span> <span class="type">int</span> num;</span><br><span class="line">        <span class="keyword">private</span> Semaphore semaphore;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Worker</span><span class="params">(<span class="type">int</span> num,Semaphore semaphore)</span>{</span><br><span class="line">            <span class="built_in">this</span>.num=num;</span><br><span class="line">            <span class="built_in">this</span>.semaphore=semaphore;</span><br><span class="line">        }</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line">            <span class="keyword">try</span> {</span><br><span class="line">                semaphore.acquire();<span class="comment">//线程申请资源，即员工申请打印机</span></span><br><span class="line">                System.out.println(<span class="string">"员工:"</span>+<span class="built_in">this</span>.num+<span class="string">"占用一个打印机..."</span>);</span><br><span class="line">                Thread.sleep(<span class="number">2000</span>);</span><br><span class="line">                System.out.println(<span class="string">"员工："</span>+<span class="built_in">this</span>.num+<span class="string">"打印完成，释放打印机"</span>);</span><br><span class="line">                semaphore.release();    <span class="comment">//线程释放资源，即员工在使用完毕后”释放打印机“</span></span><br><span class="line">            } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">            }</span><br><span class="line"></span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230811135708995.png" alt=""><figcaption>image-20230811135708995</figcaption>
</figure>
<h3 id="countdownlatch和cyclicbarrier的区别">CountDownLatch和CyclicBarrier的区别</h3>
<p>CountDownLatch和CyclicBarrier都用于实现多线程之间的相互等待，但二者的关注点不同。</p>
<p>CountDownLatch主要用于主线程等待其他子线程任务均执行完毕后再执行接下来的业务逻辑单元，而CyclicBarrier主要用于一组线程互相等待各线程都达到某个状态后，再同时执行接下来的业务逻辑单元。</p>
<p>CountDownLatch是不可以重用的，而CyclicBarrier是可以重用的。</p>
<blockquote>
<p>Semaphore和Java中的锁功能类似，主要用于控制资源的并发访问。</p>
</blockquote>
<h2 id="cas比较并交换">CAS(比较并交换)</h2>
<h3 id="cas原理">CAS原理</h3>
<p><strong>CAS(Compare And Swap,比较并交换)，用于实现乐观锁，基本思想是用一个期望值和要更新的变量值进行比较，两值相等才会更新。</strong></p>
<blockquote>
<p>CAS是一个原子操作，底层依赖于一条CPU的原子指令。</p>
</blockquote>
<p>CAS(V,E,N)包含3个参数：</p>
<ul>
<li>V:要更新的变量</li>
<li>E:期望值</li>
<li>N:新值</li>
</ul>
<p>当且仅当<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.186ex;" xmlns="http://www.w3.org/2000/svg" width="6.485ex" height="1.731ex" role="img" focusable="false" viewBox="0 -683 2866.6 765"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D449" d="M52 648Q52 670 65 683H76Q118 680 181 680Q299 680 320 683H330Q336 677 336 674T334 656Q329 641 325 637H304Q282 635 274 635Q245 630 242 620Q242 618 271 369T301 118L374 235Q447 352 520 471T595 594Q599 601 599 609Q599 633 555 637Q537 637 537 648Q537 649 539 661Q542 675 545 679T558 683Q560 683 570 683T604 682T668 681Q737 681 755 683H762Q769 676 769 672Q769 655 760 640Q757 637 743 637Q730 636 719 635T698 630T682 623T670 615T660 608T652 599T645 592L452 282Q272 -9 266 -16Q263 -18 259 -21L241 -22H234Q216 -22 216 -15Q213 -9 177 305Q139 623 138 626Q133 637 76 637H59Q52 642 52 648Z"></path></g><g data-mml-node="mo" transform="translate(1046.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mi" transform="translate(2102.6,0)"><path data-c="1D438" d="M492 213Q472 213 472 226Q472 230 477 250T482 285Q482 316 461 323T364 330H312Q311 328 277 192T243 52Q243 48 254 48T334 46Q428 46 458 48T518 61Q567 77 599 117T670 248Q680 270 683 272Q690 274 698 274Q718 274 718 261Q613 7 608 2Q605 0 322 0H133Q31 0 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q146 66 215 342T285 622Q285 629 281 629Q273 632 228 634H197Q191 640 191 642T193 659Q197 676 203 680H757Q764 676 764 669Q764 664 751 557T737 447Q735 440 717 440H705Q698 445 698 453L701 476Q704 500 704 528Q704 558 697 578T678 609T643 625T596 632T532 634H485Q397 633 392 631Q388 629 386 622Q385 619 355 499T324 377Q347 376 372 376H398Q464 376 489 391T534 472Q538 488 540 490T557 493Q562 493 565 493T570 492T572 491T574 487T577 483L544 351Q511 218 508 216Q505 213 492 213Z"></path></g></g></g></svg></mjx-container></span>，才会将V的值设置为N,如果<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.186ex;" xmlns="http://www.w3.org/2000/svg" width="7.114ex" height="1.805ex" role="img" focusable="false" viewBox="0 -716 3144.6 798"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D449" d="M52 648Q52 670 65 683H76Q118 680 181 680Q299 680 320 683H330Q336 677 336 674T334 656Q329 641 325 637H304Q282 635 274 635Q245 630 242 620Q242 618 271 369T301 118L374 235Q447 352 520 471T595 594Q599 601 599 609Q599 633 555 637Q537 637 537 648Q537 649 539 661Q542 675 545 679T558 683Q560 683 570 683T604 682T668 681Q737 681 755 683H762Q769 676 769 672Q769 655 760 640Q757 637 743 637Q730 636 719 635T698 630T682 623T670 615T660 608T652 599T645 592L452 282Q272 -9 266 -16Q263 -18 259 -21L241 -22H234Q216 -22 216 -15Q213 -9 177 305Q139 623 138 626Q133 637 76 637H59Q52 642 52 648Z"></path></g><g data-mml-node="mo" transform="translate(769,0)"><path data-c="21" d="M78 661Q78 682 96 699T138 716T180 700T199 661Q199 654 179 432T158 206Q156 198 139 198Q121 198 119 206Q118 209 98 431T78 661ZM79 61Q79 89 97 105T141 121Q164 119 181 104T198 61Q198 31 181 16T139 1Q114 1 97 16T79 61Z"></path></g><g data-mml-node="mo" transform="translate(1324.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mi" transform="translate(2380.6,0)"><path data-c="1D438" d="M492 213Q472 213 472 226Q472 230 477 250T482 285Q482 316 461 323T364 330H312Q311 328 277 192T243 52Q243 48 254 48T334 46Q428 46 458 48T518 61Q567 77 599 117T670 248Q680 270 683 272Q690 274 698 274Q718 274 718 261Q613 7 608 2Q605 0 322 0H133Q31 0 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q146 66 215 342T285 622Q285 629 281 629Q273 632 228 634H197Q191 640 191 642T193 659Q197 676 203 680H757Q764 676 764 669Q764 664 751 557T737 447Q735 440 717 440H705Q698 445 698 453L701 476Q704 500 704 528Q704 558 697 578T678 609T643 625T596 632T532 634H485Q397 633 392 631Q388 629 386 622Q385 619 355 499T324 377Q347 376 372 376H398Q464 376 489 391T534 472Q538 488 540 490T557 493Q562 493 565 493T570 492T572 491T574 487T577 483L544 351Q511 218 508 216Q505 213 492 213Z"></path></g></g></g></svg></mjx-container></span>,说明已经有其他线程做了更新，当前线程什么都不做，最后CAS返回当前V的真实值。</p>
<h3 id="cas的特性-乐观锁">CAS的特性-乐观锁</h3>
<p>CAS操作采用了乐观锁的思想，总是认为自己可以成功完成操作。在有多个线程同时使用CAS操作一个变量时，只有一个会胜出并成功更新，其余均会失败。失败的线程不会被挂起，仅被告知失败，并且允许再次尝试，当然，也允许失败的线程放弃操作。基于这样的原理，CAS操作即使没有锁，也可以发现其他线程对当前线程的干扰，并进行恰当的处理。</p>
<p>CAS导致的问题：ABA问题</p>
<h3 id="aba问题怎么解决">ABA问题？怎么解决？</h3>
<p>CAS算法需要取出内存中某时刻的数据，然后在下一时刻进行比较、替换，在这个时间差内可能数据已经发生了变化，导致产生ABA问题。</p>
<p>ABA问题指第1个线程从内存的V位置取出A，这时第2个线程也从内存中取出A,并将V位置的数据首先修改为B，接着又将V位置的数据修改为A,这时第1个线程在进行CAS操作时会发现在内存中仍然是A，然后第1个线程修改成功。尽管从第1个线程的角度来说，CAS操作是成功的，但在该过程中其实V位置的数据发生了变化，只是第1个线程没有感知到，这在某些场景下可能出现过程数据不一致的问题。</p>
<p>解决方案:<strong>版本号</strong></p>
<p><strong>通过版本号(version)来解决ABA问题</strong>，具体的操作是每次在执行数据修改的时都会带上一个版本号，在预期的版本号和数据的版本号一致时就可以执行修改操作，并对版本号执行加1操作，否则执行失败。因为每次操作的版本号都会随之增加，所以不会出现ABA问题，因为版本号只会增加，不会减少。</p>
<h2 id="aqs">AQS</h2>
<h3 id="aqs的原理"><strong>AQS的原理</strong></h3>
<p><code>AQS(AbstractQueuedSynchronizer)</code>的核心思想是，如果被请求的共享资源空闲，则将当前请求资源的线程设置为有效的工作线程，并且将共享资源设置为锁定状态。如果被请求的共享资源被占用，那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制，这个机制AQS是用<strong>CLH队列锁</strong>实现的，即将暂时获取不到锁的线程加入到队列中。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815151620733.png" alt="CLH队列结构" style="zoom:50%;"></p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230811100217214.png" alt="AQS核心原理图" style="zoom: 80%;"></p>
<p>AQS维护了一个<code>volatile int</code>类型的变量，用于表示当前线程的同步状态。volatile虽然不能保证操作的原子性，但是能保证当前变量state的可见性。</p>
<p>state的访问方式有三种:<code>getState()</code>、<code>setState()</code>和<code>compareAndSetState()</code>，均是原子操作。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//返回同步状态的当前值</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">getState</span><span class="params">()</span> {</span><br><span class="line">     <span class="keyword">return</span> state;</span><br><span class="line">}</span><br><span class="line"> <span class="comment">//设置同步状态的值</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">setState</span><span class="params">(<span class="type">int</span> newState)</span> {</span><br><span class="line">     state = newState;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//原子地（CAS操作）将同步状态值设置为给定值update 如果当前同步状态的值等于expect（期望值）</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">compareAndSetState</span><span class="params">(<span class="type">int</span> expect, <span class="type">int</span> update)</span> {</span><br><span class="line">      <span class="keyword">return</span> unsafe.compareAndSwapInt(<span class="built_in">this</span>, stateOffset, expect, update);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="aqs共享资源的方式独占式和共享式">AQS共享资源的方式：独占式和共享式</h3>
<ul>
<li>独占式(Exclusive):只有一个线程能执行，具体的Java实现有<code>ReentrantLock</code>。</li>
<li>共享式(Share):多个线程可同时执行，具体的Java实现有<code>Semaphore</code>和<code>CountDownLatch</code>。</li>
</ul>
<p>自定义同步器的主要方法如下：</p>
<ul>
<li><code>isHeldExclusively()</code>:查询该线程是否正在独占资源，只有用到condition才需要去实现它。</li>
<li><code>tryAcquire(int)</code>:独占方式，尝试获取资源，成功返回true，失败返回false</li>
<li><code>tryRelease(int)</code>:独占方式，尝试释放资源，成功返回true，失败返回false</li>
<li><code>tryAcquireShared(int)</code>:共享方式，尝试获取资源：负数表示失败；0表示成功，但没有剩余可用资源；正数表示成功且有剩余可用资源。</li>
<li><code>tryReleaseShared(int)</code>:共享方式，尝试释放资源</li>
</ul>
<p><strong>ReentrantLock对AQS的独占方式实现</strong>：ReentrankLock中的state初始值为0时表示无锁状态，在线程执行<code>lock()</code>时，会调用<code>tryAcquire()</code>获取该锁将state+1,这时该线程独占ReentrantLock锁，其他线程在通过<code>tryAcquire()</code>获取锁时均会失败，直到该线程执行<code>unlock()</code>释放锁后state再次为0，其他线程才有机会获取该锁。该线程在释放锁之前可以重复获取该锁，每获取一次便会执行一次state+1，因此<strong>ReentrantLock也属于可重入锁</strong>。但获取多少次锁就要释放多少次锁，这样才能保证state最终为0。如果获取锁的次数多于释放锁的次数，则会出现该线程一直持有该锁的情况；如果获取锁的次数少于释放锁的次数，则运行中的程序会抛出锁异常。</p>
<p><strong>CountDownLatch对AQS的共享方式实现</strong>: CountDownLatch将任务分为N个子线程去执行，将state也初始化为N,N与线程的个数一致，N个子线程是并行执行的，每个子线程都在执行完后countDown()一次，state会执行CAS操作并减1。在所有子线程都执行完成(此时state=0)时会unpark()主线程，然后主线程会从await()返回，继续执行后续的动作。</p>
<h2 id="atomic原子类待补充">Atomic原子类(待补充)</h2>
<h2 id="了解锁的优化吗jdk1.6之后对锁进行了大量的优化你了解吗">了解锁的优化吗？JDK1.6之后对锁进行了大量的优化，你了解吗？</h2>
<p>在JDK1.6中，为了减少获得锁和释放锁带来的性能损耗，引入了偏向锁和轻量级锁，所得状态变成了四种，无锁状态、偏向锁状态、轻量级锁和重量级锁状态。锁的状态会随着竞争激烈逐渐升级，但通常情况下，锁的状态只能升级不能降级。</p>
<h2 id="java8新特性">Java8新特性</h2>
<h1 id="jvm">JVM</h1>
<h2 id="jvm结构规范java-se-8">JVM结构规范(Java SE 8)</h2>
<p><code>JVM(Java Virtual Machine)</code>是用于运行Java字节码的虚拟机。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812114458546.png" alt="image-20230812114458546" style="zoom:67%;"></p>
<p><code>JVM</code>包括一个<strong>类加载子系统</strong>、<strong>运行时数据区</strong>、<strong>执行引擎</strong>和<strong>本地接口库</strong>。</p>
<p><strong>运行时数据区包含程序计数器、Java虚拟机栈、Java堆、方法区和本地方法栈。</strong></p>
<p><strong>执行引擎</strong>包括即使编译器和垃圾回收器。</p>
<p><strong>本地接口库</strong>通过调用本地方法库与操作系统进行交互。JVM运行在操作系统之上，不与硬件设备直接交互。</p>
<p><strong>Java程序的具体运行过程如下</strong>：</p>
<ul>
<li>Java源文件被编译器编译成字节码文件。</li>
<li>JVM将字节码文件编译成相应操作系统的机器码。</li>
<li>机器码调用相应操作系统的本地方法库执行相应的方法。</li>
</ul>
<p>其中：</p>
<ul>
<li>类加载器子系统用于将编译好的<code>.Class</code>文件加载到<code>JVM</code>中。</li>
<li>运行时数据区用于存储在<code>JVM</code>运行过程中产生的数据。</li>
<li>执行引擎包括即使编译器和垃圾回收器，即使编译器用于将Java字节码编译成具体的机器码，垃圾回收器用于回收在运行过程中不再使用的对象。</li>
<li>本地接口库用于调用相应操作系统的本地方法库完成具体的指令操作。</li>
</ul>
<h2 id="hospot-jvm内存模型">HoSpot JVM内存模型</h2>
<blockquote>
<p>HotSpot JVM是目前使用最广泛的虚拟机，Oracle/Sun JDK、OpenJDK等都是以HotSpot JVM为核心实现的。</p>
</blockquote>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816141647750.png" alt=""><figcaption>HoSpot JVM内存模型</figcaption>
</figure>
<p><code>HotSpot JVM</code>包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、直接内存和元空间。</p>
<p><code>HotSpot JVM</code>的内存区域分为线程私有区域(程序计数器、Java虚拟机栈、本地方法栈)、线程共享区域(Java堆、元空间)和直接内存，如下图所示。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812150230204.png" alt=""><figcaption>image-20230812150230204</figcaption>
</figure>
<h3 id="程序计数器线程私有无内存溢出问题">程序计数器(线程私有，无内存溢出问题)</h3>
<p><strong>程序计数器</strong>：用于存储当前运行的线程所执行的字节码的行号指示器。一个线程某一时刻只会执行一个方法，这个正在被线程执行的方法称为当前方法。每个运行中的线程都有一个独立的程序计数器，在方法正被执行时，该方法的程序计数寄存器记录的是实时虚拟机字节码指令的地址；如果该方法执行的是<code>Native</code>方法，则程序计数器的值为空(<code>Undefined</code>)。</p>
<p><strong>程序计数器属于线程私有的内存区域，它是唯一没有内存溢出(<code>Out Of Memory</code>)的区域</strong>。</p>
<h3 id="java虚拟机栈线程私有描述java方法的执行过程">Java虚拟机栈(线程私有，描述Java方法的执行过程)</h3>
<p>Java虚拟机栈描述的是Java方法的内存模型，它在当前栈帧(<code>Current Stack Frame</code>)中存储了局部变量表、操作数栈、动态连接、方法出口等信息。同时栈帧用于存储部分运行时数据及其数据结构，处理动态链接(<code>Dynamic Linking</code>)方法的返回值和异常分派(<code>Dispatch Exception</code>)。</p>
<blockquote>
<p>栈帧待补充</p>
</blockquote>
<h3 id="本地方法栈线程私有">本地方法栈(线程私有)</h3>
<p>本地方法栈与Java虚拟机栈的作用类似，唯一不同的是本地方法栈执行的是<code>Native</code>方法，而虚拟机栈是为JVM执行Java方法服务的。JVM通过本地方法栈来支持<code>Native</code>方法,以调用其他语言(如C语言)实现指令集解释器。</p>
<p>与Java虚拟机栈一样，本地方法栈也会抛出<code>StackOverflowError</code>和<code>OutOfMemoryError</code>异常。</p>
<h3 id="java堆线程共享">Java堆(线程共享)</h3>
<p>在JVM运行过程中创建和产生的数据都被存储在堆中，堆在虚拟机启动时创建，包含了所有垃圾回收器所管理的对象。堆是被线程共享的内存区域，也是垃圾回收器进行垃圾回收的最主要的区域。</p>
<p>由于现代JVM采用<strong>分代回收算法</strong>，因此Java堆从<code>GC(Garbage Collection,垃圾回收)</code>的角度还可以细分为:<strong>新生代</strong>、<strong>老年代</strong>。</p>
<h3 id="元空间线程共享方法区在hotspot-jvm中的实现">元空间(线程共享，方法区在HotSpot JVM中的实现)</h3>
<p>元空间是方法区在HotSpot JVM中的实现。方法区与传统语言的编译代码存储区类似，存储了类的结构信息，具体包括：运行时常量、字段、方法和方法的数据、构造函数、初始化类信息(包括普通方法字节码和初始化类时用到的一些特殊方法)，如下图所示。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816142350335.png" alt=""><figcaption>image-20230816142350335</figcaption>
</figure>
<blockquote>
<p>注意，元空间使用的内存并不在虚拟机中，而是直接使用的本地内存，其大小取决于操作系统可使用的内存大小。我们可以通过<code>MetaspaceSize</code>设置初始化的元空间大小，<code>MetaspaceSize</code>的默认值为12MB~20MB(对应不同的平台)。</p>
</blockquote>
<h3 id="直接内存">直接内存</h3>
<p>直接内存也叫做堆外内存，并不是JVM运行时数据区的一部分，但在并发编程过程中被频繁使用。</p>
<p>JDK的<code>NIO(Non-Blocking I/O)</code>模块提供的基于<code>Channel</code>与<code>Buffer</code>的I/O操作方式就是基于对外内存实现的。NIO模块通过调用<code>Native</code>函数库直接在操作系统上分配堆外内存，然后使用<code>DirectByteBuffer</code>对象作为这块内存的应用对内存进行操作，Java进程可以通过堆外内存技术避免在Java堆和Native堆中来回复制数据带来的资源浪费和性能损耗，因此堆外内存在高并发应用场景下被广泛使用(<code>Netty</code>、<code>Flink</code>、<code>HBase</code>、<code>Hadoop</code>都有用到堆外内存)。</p>
<h3 id="运行时数据区">运行时数据区</h3>
<p>Java虚拟机在执行Java程序的过程中会把它管理的内存划分成若干个不同的数据区域。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812123654014.png" alt="image-20230812123654014" style="zoom: 80%;"></p>
<p><strong>JVM运行时数据区主要由方法区、堆、虚拟机栈、本地方法栈、程序计数器组成</strong>。</p>
<p>线程私有：程序计数器、Java虚拟机栈、本地方法栈</p>
<p>线程共享：Java堆、元空间和直接内存</p>
<h3 id="对象是如何创建的">对象是如何创建的</h3>
<p>对象的创建主要有如下几个过程：</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816142541632.png" alt=""><figcaption>image-20230816142541632</figcaption>
</figure>
<ol type="1">
<li><strong>类加载检查</strong>：虚拟机遇到一条<code>new</code>指令时，首先会去检查这个指令的参数是否能在常量池中定位到这个类的符号引用，并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有，那必须先执行相应的类加载过程。</li>
<li><strong>分配内存</strong>：在类加载检查后，就要为新生对象分配内存了，对象内存所需大小在类加载完成后便可以确定，内存分配方式根据Java堆中内存是否完整主要分为<strong>指针碰撞</strong>和<strong>空闲列表</strong>两种。</li>
<li><strong>初始化零值</strong>：内存分配完成后，虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头)，这也是为什么字段在Java代码中可以不赋值就能直接使用的原因。</li>
<li><strong>设置对象头</strong>：初始化零值后，虚拟机需要对对象进行必要的设置，例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息都是存放在对象的对象头中。根据虚拟机当前的运行状态不同，如是否使用偏向锁等，对象头都会有不同的设置方式。</li>
<li><strong>执行<code>init</code>方法</strong>：上述操作完成后，从虚拟机的角度看，一个新的对象已经产生了。但从Java程序的角度看，对象创建才刚刚开始，<code>&lt;init&gt;</code>方法还没有执行，所有的字段都还为零。所以，一般执行完<code>new</code>指令后还会接着执行<code>&lt;init&gt;</code>方法，把对象按照程序员的意愿进行初始化(赋值)，这样一个真正可用的对象才算生产出来。</li>
</ol>
<h3 id="创建对象时内存是如何分配的">创建对象时内存是如何分配的</h3>
<p>创建对象的内存分配方式会根据Java内存是否完整分为<strong>指针碰撞(完整)</strong>和<strong>空闲列表(不完整)</strong>两种：</p>
<ul>
<li><p><strong>指针碰撞</strong>：假设为<code>Java</code>堆中内存是绝对完整的，所有用过的内存放到一边，空闲的内存放到另一边，中间放着一个指针作为分界点的指示器，所分配的内存就是把那个指针向空闲空间那边挪动一段与对象大小相等的距离，这种分配方式称为指针碰撞。</p></li>
<li><p><strong>空闲列表</strong>：假设<code>Java</code>堆中的内存并不是完整的，已使用的内存和空闲内存都混在一起了，这时虚拟机需要维护一个列表，用来记录哪些内存块是可用的，在分配的时候从列表中找到一块足够大的空间划分给对象实例，并更新列表上的记录，这种分配方式称为空闲列表</p></li>
</ul>
<blockquote>
<p>注：选择哪种分配方式由Java堆是否完整决定，Java堆是否完整由所采用的垃圾收集器是否带有压缩整理功能决定。因此，在使用<code>Serial、ParNew</code>等垃圾收集器时系统采用的是指针碰撞，在使用<code>CMS</code>等基于标记擦除算法的收集器时，采用的是空闲列表。</p>
</blockquote>
<h2 id="jvm的类加载机制">JVM的类加载机制</h2>
<p>JVM的类加载机制指的是把JVM编译好的<code>Class</code>文件以<strong>加载、验证、准备、解析、初始化</strong>的步骤加载到内存中，使其能够直接被JVM使用。</p>
<h3 id="类的生命周期">类的生命周期</h3>
<p>类从加载到虚拟机内存开始，到卸载出内存为止，声明周期包括：<strong>加载、验证、准备、解析、初始化、使用</strong>和<strong>卸载</strong>7个部分，其中验证、准备、解析统称为连接。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812155600807.png" alt="image-20230812155600807" style="zoom: 80%;"></p>
<h3 id="jvm的类加载阶段">JVM的类加载阶段</h3>
<p><strong>1、加载</strong></p>
<ul>
<li>通过一个类的全限定名类获取定义此类的二进制字节流</li>
<li>将这个字节流所代表的静态存储结构转化为方法去的运行时数据结构</li>
<li>在内存中生成一个代表这个类得到<code>java.lang.Class</code>对象，作为方法区这个类的各种数据结构的访问入口。</li>
</ul>
<p><strong>2、验证</strong></p>
<p>主要用于确保<code>Class</code>文件符合当前虚拟机的要求，保障虚拟机自身的安全，只有通过验证的<code>Class</code>文件才能被JVM加载。</p>
<ul>
<li>文件格式验证(Class文件格式检查)</li>
<li>元数据验证(字节码语义检查)</li>
<li>字节码验证(程序语义检查)</li>
<li>符号引用验证(类的正确性检查)</li>
</ul>
<p><strong>3、准备</strong></p>
<p>主要工作是在方法区中为类变量分配内存空间并设置类中变量的初始值。初始值指不同数据类型的默认值，这里需要注意<code>final</code>类型的变量和非<code>final</code>类型的变量在准备阶段的数据初始化过程不同。</p>
<p>比如一个成员变量的定义为：<code>public static long value=1000</code>,静态变量value在准备阶段的初始值是0，将value设置为1000的动作是在对象初始化时完成的，因为JVM在编译阶段会将静态变量的初始化操作定义在构造器中。</p>
<p>但是如果将变量声明为<code>final</code>类型:<code>public static final long value=1000</code>,则JVM会在编译阶段后为final类型的变量value生成其对应的<code>ConstantValue</code>属性，虚拟机在准备阶段会根据<code>ConstantValue</code>属性将value赋值为1000。</p>
<p><strong>4、解析</strong></p>
<p>JVM会将常量池中的符号引用替换为直接引用。</p>
<p><strong>5、初始化</strong></p>
<p>主要通过执行类构造器的<code>&lt;clinit&gt;</code>方法将类初始化。<code>&lt;clinit&gt;</code>方法是在编译阶段由编译器自动收集类中静态语句块和变量的赋值操作组成的。在JVM中规定，只有在父类的<code>&lt;clinit&gt;</code>方法都执行成功后，子类的<code>&lt;clinit&gt;</code>方法才可被执行。在一个类中既没有静态变量赋值操作也没有静态语句块时，编译器不会为该类生成<code>&lt;clinit&gt;</code>方法。</p>
<h3 id="类加载器">类加载器</h3>
<p>JVM提供了3种类加载器，分别是<strong>启动类加载器</strong>、<strong>扩展类加载器</strong>和<strong>应用程序类加载器</strong>。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816143711131.png" alt="类加载器" style="zoom:80%;"></p>
<p>(1)启动类加载器：负责加载<code>JAVA_HOME/lib</code>目录下的类库，或通过<code>-Xbootclasspath</code>参数指定路径下被虚拟机认可的类库。</p>
<p>(2)扩展类加载器：负责加载<code>JAVA_HOME/lib/ext</code>目录下的类库，或通过<code>java.ext.dirs</code>系统变量加载指定路径下的类库。</p>
<p>(3)应用程序类加载器：负责加载用户路径(<code>classpath</code>)下的类库。</p>
<p>除了上述3种类加载器，我也可以通过继承<code>java.lang.ClassLoader</code>实现自定义类加载器。</p>
<h3 id="双亲委派机制">双亲委派机制(*)</h3>
<p><strong>双亲委派机制</strong>是指一个类加载器在收到类加载请求后不会尝试自己加载这个类，而是把这个请求向上委托给其父类加载器去完成，其父类加载器在接收到该类加载请求后又会将其委派给自己的父类加载器，以此类推，这样所有的类加载请求都被向上委派到启动类加载器中。如果父类加载器在接收到类加载请求后发现自己也无法加载该类(通常原因是该类的Class文件在父类的类加载路径下不存在)，则父类会将该信息反馈给子类并向下委派子类加载器加载该类，直到该类给加载成功，如果找不到该类，则JVM会抛出<code>ClassNotFound</code>异常。</p>
<p>双亲委派机制的类加载流程如下，如图所示：</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816144446451.png" alt=""><figcaption>双亲委派机制的类加载流程</figcaption>
</figure>
<p>(1)将自定义类加载器挂载到应用程序类加载器。</p>
<p>(2)应用程序类加载器将类加载请求委托给扩展类加载器。</p>
<p>(3)扩展类加载器将类加载请求委托给启动类加载器。</p>
<p>(4)启动类加载器在加载路径下查找并加载Class文件，如果未找到目标Class文件，则交由扩展类加载器加载。</p>
<p>(5)扩展类加载器在加载路径下查找并加载Class文件，如果未找到目标Class文件，则交由应用程序类加载器加载。</p>
<p>(6)应用程序类加载器在加载路径下查找并加载Class文件，如果未找到目标Class文件，则交由自定义类加载器加载。</p>
<p>(7)自定义类加载器在自定义加载路径下查找并加载用户指定目录下的Class文件，如果未找到目标Class文件，则抛出<code>ClassNotFound</code>异常。</p>
<h4 id="类加载器中的父类加载器和子类加载器是继承关系吗">类加载器中的父类加载器和子类加载器是继承关系吗？</h4>
<p>类加载器之间的父子关系一般不是以继承关系实现的，而是<strong>组合</strong>的关系来复用父加载器的代码。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">ClassLoader</span> {</span><br><span class="line">  ...</span><br><span class="line">  <span class="comment">// 组合</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> ClassLoader parent;</span><br><span class="line">  <span class="keyword">protected</span> <span class="title function_">ClassLoader</span><span class="params">(ClassLoader parent)</span> {</span><br><span class="line">       <span class="built_in">this</span>(checkCreateClassLoader(), parent);</span><br><span class="line">  }</span><br><span class="line">  ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="双亲委派机制的好处">双亲委派机制的好处</h4>
<p>保证了Java程序稳定地运行，可以<strong>避免类的重复加载</strong>(父类加载器加载过，子加载器不会再进行加载)，<strong>保证Java的核心API不被篡改</strong>。</p>
<h4 id="如何打破双亲委派机制">如何打破双亲委派机制</h4>
<p>自定义一个类加载器(集成<code>ClassLoader</code>)，重写其中的<code>loadClass()</code>方法即可。</p>
<p>例子：我们比较熟悉的Tomcat服务器为了能够优先加载Web应用目录下的类，然后再加载其他目录下的类，就自定义类加载器<code>WebAppClassLoader</code>来打破双亲委派机制。这也是Tomcat下Web应用之间的类实现隔离的具体原理。</p>
<blockquote>
<p>为什么是重写<code>loadClass()</code>方法打破双亲委派机制呢？</p>
<p>类加载器在进行类加载的时候，它首先不会自己去尝试加载这个类，而是把这个请求委派给父类加载器去完成(调用父类加载器的<code>loadClass()</code>方法来加载类)。</p>
</blockquote>
<h2 id="hotspot-jvm堆">HotSpot JVM堆</h2>
<p>从GC的角度可以将HotSpot JVM堆分为<strong>新生代</strong>、<strong>老年代</strong>。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816145753117.png" alt="image-20230816145753117" style="zoom:80%;"></p>
<p>新生代默认占<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.816ex;" xmlns="http://www.w3.org/2000/svg" width="1.795ex" height="2.773ex" role="img" focusable="false" viewBox="0 -864.9 793.6 1225.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mn" transform="translate(220,394) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mn" transform="translate(220,-345) scale(0.707)"><path data-c="33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path></g><rect width="553.6" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></span>堆空间，老年代默认占<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.816ex;" xmlns="http://www.w3.org/2000/svg" width="1.795ex" height="2.773ex" role="img" focusable="false" viewBox="0 -864.9 793.6 1225.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mn" transform="translate(220,394) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path></g><g data-mml-node="mn" transform="translate(220,-345) scale(0.707)"><path data-c="33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path></g><rect width="553.6" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></span>堆空间，新生代分为<code>Eden</code>区、<code>SurvivorFrom</code>区和<code>SurvivorTo</code>区，默认比例<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.05ex;" xmlns="http://www.w3.org/2000/svg" width="7.165ex" height="1.557ex" role="img" focusable="false" viewBox="0 -666 3167.1 688"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mn"><path data-c="38" d="M70 417T70 494T124 618T248 666Q319 666 374 624T429 515Q429 485 418 459T392 417T361 389T335 371T324 363L338 354Q352 344 366 334T382 323Q457 264 457 174Q457 95 399 37T249 -22Q159 -22 101 29T43 155Q43 263 172 335L154 348Q133 361 127 368Q70 417 70 494ZM286 386L292 390Q298 394 301 396T311 403T323 413T334 425T345 438T355 454T364 471T369 491T371 513Q371 556 342 586T275 624Q268 625 242 625Q201 625 165 599T128 534Q128 511 141 492T167 463T217 431Q224 426 228 424L286 386ZM250 21Q308 21 350 55T392 137Q392 154 387 169T375 194T353 216T330 234T301 253T274 270Q260 279 244 289T218 306L210 311Q204 311 181 294T133 239T107 157Q107 98 150 60T250 21Z"></path></g><g data-mml-node="mo" transform="translate(777.8,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mn" transform="translate(1333.6,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(2111.3,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mn" transform="translate(2667.1,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g></svg></mjx-container></span>,老年代就一个<code>Old Memory</code>区。</p>
<h2 id="jvm垃圾回收的原理与算法">JVM垃圾回收的原理与算法</h2>
<p>JVM采用垃圾回收机制对不再使用的对象所占用的内存空间进行清理，以保障JVM有足够的空间持续存放新创建的对象。</p>
<h3 id="如何确定垃圾">如何确定垃圾？</h3>
<p>Java采用<strong>引用计数法</strong>和<strong>可达性分析</strong>来确定对象是否应该被回收。</p>
<h4 id="引用计数法">(1)引用计数法</h4>
<p>在Java中如果要操作对象，就必须先获取该对象的引用，可以给对象添加一个引用计数器，每当有一个地方引用它时，计数器加1，当引用失效，计数器就减1，如果一个对象的应用计数为0，则表示此时该对象没有被应用，可被回收。</p>
<p><strong>引用计数法容易产生循环引用问题</strong>。循环引用指两个对象互相引用，导致它们的引用一直存在，而不能被回收，如下图所示，Object1与Object2互为引用，如果采用引用计数法，则Object1和Object2由于互为引用，则引用计数一致为1，因而无法被回收。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230812175441516.png" alt=""><figcaption>image-20230812175441516</figcaption>
</figure>
<h4 id="可达性分析">(2)可达性分析</h4>
<p><strong>首先定义一些<code>GC Roots</code>对象,然后以这些<code>GC Roots</code>对象为起点向下搜索，如果在<code>GC Roots</code>和一个对象之间没有可达路径，则该对象是不可达的</strong>。不可达对象要经过至少两次标记才能判定其是否可被回收，如果在两次标记后该对象仍然是不可达的，则将被垃圾回收器回收。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816150748221.png" alt="image-20230816150748221" style="zoom:80%;"></p>
<blockquote>
<p>上图中的Object6~Object10之间虽然有引用关系，但它们到GC Roots不可达，因此会被判定是可回收的。</p>
</blockquote>
<p><strong>哪些对象可以作为GC Roots？</strong></p>
<ul>
<li>虚拟机栈(栈帧中的本地变量表)中引用的对象</li>
<li>本地方法栈(Native方法)中引用的对象</li>
<li>方法区中类静态属性引用的对象</li>
<li>方法区中常量引用的对象</li>
<li>所有被同步锁持有的对象</li>
<li>JNI(Java Native Interface)引用的对象</li>
</ul>
<h3 id="java中的四种引用类型">Java中的四种引用类型</h3>
<p>在Java中一切皆对象，对象的操作是通过该对象的引用(<code>Reference</code>)实现的，Java的引用有四种类型，分别为<strong>强引用、软引用、弱引用</strong>和<strong>虚引用</strong>。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816150957878.png" alt="image-20230816150957878" style="zoom:80%;"></p>
<ul>
<li><strong>强引用</strong>：在把一个对象赋值给一个引用变量时，这个引用变量就是一个强引用。<strong>有强引用的对象一定为可达性状态，所以不会被垃圾回收机制回收。强引用是造成Java内存泄漏(<code>Memory Leak</code>)的主要原因</strong>。</li>
<li><strong>软引用</strong>：通过<code>SoftReference</code>类实现。如果一个对象只有软引用，则在系统内存空间不足时该对象将被回收。</li>
<li><strong>弱引用</strong>：通过<code>WeakReference</code>类实现，如果一个对象只有弱引用，则在垃圾回收过程中一定会被回收。</li>
<li><strong>虚引用</strong>：虚引用通过<code>PhantomReference</code>类实现，虚引用和引用队列联合使用，主要用于跟踪对象的垃圾回收状态。</li>
</ul>
<h3 id="垃圾回收算法">垃圾回收算法</h3>
<h4 id="标记-清除算法mark-sweep">(1)标记-清除算法(Mark-Sweep)</h4>
<p>标记-清除(Mark-Sweep)算法分为"标记(Mark)"和"清除(Sweep)"：<strong>在标记阶段标记所有需要回收的对象，在清除阶段清除可回收的对象并释放其所占用的内存空间</strong>。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/mark-and-sweep-garbage-collection-algorithm.png" alt=""><figcaption>标记-清除算法</figcaption>
</figure>
<p>这是最基础的垃圾回收算法，带来的问题：</p>
<ul>
<li><strong>效率问题</strong>：标记和清除两个过程效率都不高。</li>
<li><strong>空间问题</strong>：标记清除后会产生大量不连续的内存碎片。</li>
</ul>
<blockquote>
<p>标记清除算法在清理对象所占用的内存空间后没有整理可用的内存空间，因此会导致内存碎片化问题。</p>
</blockquote>
<h4 id="复制算法coping">(2)复制算法(Coping)</h4>
<p>复制算法是为了解决标记清除算法的内存碎片化问题。它将内存分为大小相同的两块，每次使用其中的一块。当这一块的内存使用完后，就将还存活的对象复制到另一块去，然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/copying-garbage-collection-algorithm.png" alt=""><figcaption>复制算法</figcaption>
</figure>
<p><strong>存在的问题：</strong></p>
<ul>
<li><strong>可用内存变小</strong>：可用内存缩小为原来的一般。</li>
<li><strong>不适合老年代</strong>：如果存活对象数量比较大，复制性能会变得很差。</li>
</ul>
<h4 id="标记整理算法mark-compact">(3)标记整理算法(Mark-Compact)</h4>
<p>该算法结合了标记清除算法和复制算法的优点，标记阶段和标记清除算法的标记阶段相同，在标记完成后将存活的对象移动到内存的另一端，然后清除该端的对象并释放内存。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815172544300.png" alt=""><figcaption>image-20230815172544300</figcaption>
</figure>
<h4 id="分代回收算法generational-collecting">(4)分代回收算法(Generational Collecting)</h4>
<p>当前虚拟机的垃圾收集都采用分代收集算法，这种算法没什么新的思想，只是根据对象存活周期的不同将内存分为几块。一般将Java堆分为新生代和老年代，这样我们就可以根据各年代的特性分别采用不同的<code>GC</code>(垃圾收集)算法。</p>
<p><strong>新生代与复制算法</strong>：新生代主要存储短生命周期的对象，在垃圾回收的标记阶段会标记大量已死亡的对象及少量存活的对象，因此只需选用<strong>复制算法</strong>将少量存活的对象复制到内存的另一端并清理原区域的内存即可。</p>
<p><strong>老年代与标记整理算法</strong>：老年代主要存放长生命周期的对象和大对象，可回收的对象一般较少，因此JVM采用<strong>标记整理算法</strong>进行垃圾回收，直接释放死亡状态的对象所占用的内存空间。</p>
<h3 id="垃圾回收器">垃圾回收器</h3>
<p>如果说回收算法是内存回收的方法论，那么垃圾回收器就是内存回收的具体实现。JVM针对新生代提供的垃圾回收器有<code>Serial</code>、<code>ParNew</code>、<code>Parallel Scavenge</code>，针对老年代提供的垃圾回收器有<code>Serial Old</code>、<code>Parallel Old</code>、<code>CMS</code>，还有针对不同区域的<code>G1</code>分区回收算法，如下图所示。</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816112645827.png" alt="image-20230816112645827" style="zoom: 80%;"></p>
<h4 id="serial回收器单线程复制算法">Serial回收器：单线程，复制算法</h4>
<p>Serial垃圾回收器基于复制算法实现，它是一个单线程回收器，在它进行垃圾回收时，必须暂停其他所有工作线程，直到垃圾回收结束。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/serial-garbage-collector.png" alt=""><figcaption>Serial</figcaption>
</figure>
<p>Serial垃圾回收器采用了复制算法，其特性是：<strong>简单、高效</strong>，对于单CPU运行环境来说，没有线程交互开销，可以获得最高的单线程垃圾回收效率，因此<code>Serial</code>垃圾回收器是<code>Java</code>虚拟机运行在客户端模式下的新生代的默认垃圾回收器。</p>
<h4 id="parnew回收器多线程复制算法">ParNew回收器：多线程，复制算法</h4>
<p>ParNew垃圾回收器是Serial垃圾回收器的多线程实现，同样使用了复制算法，除了采用多线程之外和Serial垃圾回收器几乎一样。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/parnew-garbage-collector.png" alt=""><figcaption>ParNew</figcaption>
</figure>
<p>ParNew在垃圾回收过程中会暂停其他所有工作线程，是Java虚拟机运行在Server模式下的新生代的默认垃圾回收器。</p>
<h4 id="parallel-scavenge回收器多线程复制算法">Parallel Scavenge回收器：多线程，复制算法</h4>
<p>Parallel Scavenge垃圾回收器是为提高新生代垃圾回收效率而设计的垃圾回收器，基于多线程复制算法实现，在系统吞吐量上有很大的优化，可以更高效地利用CPU尽快完成垃圾回收任务。</p>
<p>Parallel Scavenge通过自适应调节策略提高系统吞吐量，提供了三个参数用于调节、控制垃圾回收地停顿时间及吞吐量</p>
<ul>
<li><code>-XX:MaxGCPauseMillis</code>:控制最大垃圾回收停顿时间</li>
<li><code>-XX:GCTimeRatio</code>:控制吞吐量大小</li>
<li><code>UseAdaptiveSizePolicy</code>:控制自适应调节策略是否开启</li>
</ul>
<h4 id="serial-old回收器单线程标记整理算法">Serial Old回收器:单线程，标记整理算法</h4>
<p><strong>Serial收集器的老年代版本</strong>，同Serial一样采用单线程执行，不同的是，Serial Old针对老年代长生命周期的特性基于标记整理算法实现。</p>
<p>新生代的Serial垃圾回收器和老年代的Serial Old垃圾回收器可搭配使用，分别针对JVM的新生代和老年代进行垃圾回收，回收过程如下图所示。在新生代采用Serial垃圾回收器基于复制算法进行垃圾回收，未被其回收的对象在老年代被Serial Old垃圾回收器基于标记整理算法进行垃圾回收。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/serial-garbage-collector.png" alt=""><figcaption>Serial/Serial Old</figcaption>
</figure>
<h4 id="parallel-old垃圾回收器多线程标记整理算法">Parallel Old垃圾回收器：多线程，标记整理算法。</h4>
<p><strong>Parallel Scavenge收集器的老年代版本</strong>。使用多线程和标记整理算法，优先考虑系统吞吐量，其次考虑停顿时间等因素。</p>
<p>新生代的<code>Parallel Scavenge</code>垃圾回收器和老年代的<code>Parallel Old</code>垃圾回收器的搭配运行如下，新生代基于<code>Parallel Scanenge</code>的复制算法进行回收，老年代基于<code>Parallel Old</code>的标记整理算法进行回收。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/parallel-scavenge-garbage-collector.png" alt=""><figcaption>Parallel Scavenge/Parallel Old</figcaption>
</figure>
<h4 id="cms垃圾回收器">CMS垃圾回收器</h4>
<p><code>CMS(Concurrent Mark Sweep)</code>是为老年代设计的垃圾回收器，其主要目的是用最短的垃圾回收停顿时间完成垃圾回收，基于多线程的标记清除算法实现，以便在多线程并发环境下以最短的垃圾回收停顿时间提高系统的稳定性。</p>
<p>CMS工作机制相对复杂，垃圾回收过程如下：</p>
<p><strong>(1)初始标记：</strong>只标记和<code>GC Roots</code>直接关联的对象，速度很快，需要暂停所有工作线程。</p>
<p><strong>(2)并发标记：</strong>和用户线程一起工作，执行<code>GC Roots</code>跟踪标记过程，不需要暂停工作线程。</p>
<p><strong>(3)重新标记：</strong>在并发标记过程中用户线程继续运行，导致在垃圾回收过程中部分对象的状态发生变化，为了确保这部分对象的状态正确。需要对其重新标记并暂停工作线程。</p>
<p><strong>(4)并发清除：</strong>和用户线程一起工作，执行清除<code>GC Roots</code>不可达对象的任务，不需要暂停工作线程。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/cms-garbage-collector.png" alt=""><figcaption>CMS垃圾回收器工作流程</figcaption>
</figure>
<p>CMS垃圾回收器在和用户线程一起工作时(并发标记和并发清除)不需要暂停用户线程，有效缩短了垃圾回收时系统的停顿时间，同时由于CMS垃圾回收器和用户线程一起工作，因此其并行度和效率也有很大提升。</p>
<h4 id="g1垃圾回收器">G1垃圾回收器</h4>
<p><code>G1(Garbage First)</code>垃圾回收器为了避免全区域垃圾回收引起的系统停顿，将堆内存划分为大小固定的几个独立区域，独立使用这些区域的内存资源并且跟踪这些区域的垃圾回收进度，同时在后台维护一个优先级列表，在垃圾回收过程中根据系统允许的最长垃圾回收时间，优先回收垃圾最多的区域。</p>
<p>G1垃圾回收器通过内存区域独立划分使用和根据不同优先级回收各区域垃圾的机制，确保了G1垃圾回收器在有限时间内获得最高的垃圾回收效率。相对于<code>CMS</code>垃圾回收器，<code>G1</code>垃圾回收器有以下两个突出的改进：</p>
<ul>
<li><strong>基于标记整理算法，不产生内存碎片</strong>。</li>
<li><strong>可以精确地控制停顿时间，在不牺牲吞吐量地前提下实现最短停顿垃圾回收</strong>。</li>
</ul>
<h2 id="jvm启动参数设置">JVM启动参数设置</h2>
<p>JVM的内存参数众多，但在实际应用中主要关注堆内存的大小设置及堆内存中新生代和老年代的大小设置。</p>
<p>下面看一个简单的JVM启动参数设置案例：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">java -server</span><br><span class="line">-Xms3g -Xmx3g	<span class="comment">#初始堆大小3GB，最大堆大小3GB</span></span><br><span class="line">-XX:NewSize=1g	<span class="comment">#新生代大小1GB</span></span><br><span class="line">-XX:MetaspaceSize=128m	<span class="comment">#元空间大小128MB</span></span><br><span class="line">-XX:NewRatio=3			<span class="comment">#新生代与老年代比值1:3</span></span><br><span class="line">-XX:SurvivorRatio=8		<span class="comment">#Eden:SurvivorTo:SurvivorFrom=8:1:1</span></span><br><span class="line">-XX:+UseParNewGC -XX:+UseConcMarkSweepGC <span class="comment">#新生代使用ParNewGC,老年代使用CMS</span></span><br><span class="line">-XX:+HeapDumpOnOutOfMemoryError	<span class="comment">#在发生OOM时打印日志</span></span><br><span class="line">-XX:HeapDumpPath=dump.log		<span class="comment">#OOM日志存储地址</span></span><br><span class="line">-jar start.jar</span><br></pre></td></tr></table></figure>
<p>在以上代码中执行Java命令启动了一个名为start的<code>JAR</code>程序，并在启动时设置了一些常用的参数。</p>
<p>(1)<code>-Xms -Xmx</code>:<code>-Xms</code>表示初始堆大小；<code>-Xmx</code>表示最大堆大小，一般将二者设置为相同的值，避免垃圾回收后JVM重新分配堆内存大小而引起的内存震荡，影响性能。可将堆内存的大小简单理解为JVM在运行过程中可用到的总内存大小。</p>
<p>(2)<code>-XX:NewSize</code>:<code>-XX:NewSize=1g</code>表示设置新生代的大小为1GB，一般建议设置为总堆内存的<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.816ex;" xmlns="http://www.w3.org/2000/svg" width="1.795ex" height="2.773ex" role="img" focusable="false" viewBox="0 -864.9 793.6 1225.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mn" transform="translate(220,394) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mn" transform="translate(220,-345) scale(0.707)"><path data-c="33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path></g><rect width="553.6" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></span>。</p>
<p>(3)<code>-XX:MetasoaceSize</code>:<code>-XX:MetaspaceSize=128m</code>表示元空间的大小为128MB,当要加载的类库过多时，可以适当调高这个值。</p>
<p>(4):<code>-XX:NewRatio</code>:<code>-XX:NewRatio=3</code>表示设置新生代与老年代的比值为<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.05ex;" xmlns="http://www.w3.org/2000/svg" width="4.148ex" height="1.557ex" role="img" focusable="false" viewBox="0 -666 1833.6 688"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mn"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(777.8,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mn" transform="translate(1333.6,0)"><path data-c="33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path></g></g></g></svg></mjx-container></span>,因此新生代占整个堆栈的<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.781ex;" xmlns="http://www.w3.org/2000/svg" width="1.795ex" height="2.737ex" role="img" focusable="false" viewBox="0 -864.9 793.6 1209.9"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mn" transform="translate(220,394) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mn" transform="translate(220,-345) scale(0.707)"><path data-c="34" d="M462 0Q444 3 333 3Q217 3 199 0H190V46H221Q241 46 248 46T265 48T279 53T286 61Q287 63 287 115V165H28V211L179 442Q332 674 334 675Q336 677 355 677H373L379 671V211H471V165H379V114Q379 73 379 66T385 54Q393 47 442 46H471V0H462ZM293 211V545L74 212L183 211H293Z"></path></g><rect width="553.6" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></span>,老年代占整个堆内存的<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.781ex;" xmlns="http://www.w3.org/2000/svg" width="1.795ex" height="2.736ex" role="img" focusable="false" viewBox="0 -864.2 793.6 1209.2"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mn" transform="translate(220,394) scale(0.707)"><path data-c="33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path></g><g data-mml-node="mn" transform="translate(220,-345) scale(0.707)"><path data-c="34" d="M462 0Q444 3 333 3Q217 3 199 0H190V46H221Q241 46 248 46T265 48T279 53T286 61Q287 63 287 115V165H28V211L179 442Q332 674 334 675Q336 677 355 677H373L379 671V211H471V165H379V114Q379 73 379 66T385 54Q393 47 442 46H471V0H462ZM293 211V545L74 212L183 211H293Z"></path></g><rect width="553.6" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></span>。</p>
<p>(5)<code>-XX:SurvivorRatio</code>:<code>-XX:SurvivorRatio=8</code>表示Eden区和两个Survivor区(SurvivorTo区、SurvivorFrom区)的比值为<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.05ex;" xmlns="http://www.w3.org/2000/svg" width="4.148ex" height="1.557ex" role="img" focusable="false" viewBox="0 -666 1833.6 688"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mn"><path data-c="38" d="M70 417T70 494T124 618T248 666Q319 666 374 624T429 515Q429 485 418 459T392 417T361 389T335 371T324 363L338 354Q352 344 366 334T382 323Q457 264 457 174Q457 95 399 37T249 -22Q159 -22 101 29T43 155Q43 263 172 335L154 348Q133 361 127 368Q70 417 70 494ZM286 386L292 390Q298 394 301 396T311 403T323 413T334 425T345 438T355 454T364 471T369 491T371 513Q371 556 342 586T275 624Q268 625 242 625Q201 625 165 599T128 534Q128 511 141 492T167 463T217 431Q224 426 228 424L286 386ZM250 21Q308 21 350 55T392 137Q392 154 387 169T375 194T353 216T330 234T301 253T274 270Q260 279 244 289T218 306L210 311Q204 311 181 294T133 239T107 157Q107 98 150 60T250 21Z"></path></g><g data-mml-node="mo" transform="translate(777.8,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mn" transform="translate(1333.6,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g></svg></mjx-container></span>,即<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.186ex;" xmlns="http://www.w3.org/2000/svg" width="45.493ex" height="1.781ex" role="img" focusable="false" viewBox="0 -705 20107.8 787"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D438" d="M492 213Q472 213 472 226Q472 230 477 250T482 285Q482 316 461 323T364 330H312Q311 328 277 192T243 52Q243 48 254 48T334 46Q428 46 458 48T518 61Q567 77 599 117T670 248Q680 270 683 272Q690 274 698 274Q718 274 718 261Q613 7 608 2Q605 0 322 0H133Q31 0 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q146 66 215 342T285 622Q285 629 281 629Q273 632 228 634H197Q191 640 191 642T193 659Q197 676 203 680H757Q764 676 764 669Q764 664 751 557T737 447Q735 440 717 440H705Q698 445 698 453L701 476Q704 500 704 528Q704 558 697 578T678 609T643 625T596 632T532 634H485Q397 633 392 631Q388 629 386 622Q385 619 355 499T324 377Q347 376 372 376H398Q464 376 489 391T534 472Q538 488 540 490T557 493Q562 493 565 493T570 492T572 491T574 487T577 483L544 351Q511 218 508 216Q505 213 492 213Z"></path></g><g data-mml-node="mi" transform="translate(764,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="mi" transform="translate(1284,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"></path></g><g data-mml-node="mi" transform="translate(1750,0)"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(2627.8,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mi" transform="translate(3183.6,0)"><path data-c="1D446" d="M308 24Q367 24 416 76T466 197Q466 260 414 284Q308 311 278 321T236 341Q176 383 176 462Q176 523 208 573T273 648Q302 673 343 688T407 704H418H425Q521 704 564 640Q565 640 577 653T603 682T623 704Q624 704 627 704T632 705Q645 705 645 698T617 577T585 459T569 456Q549 456 549 465Q549 471 550 475Q550 478 551 494T553 520Q553 554 544 579T526 616T501 641Q465 662 419 662Q362 662 313 616T263 510Q263 480 278 458T319 427Q323 425 389 408T456 390Q490 379 522 342T554 242Q554 216 546 186Q541 164 528 137T492 78T426 18T332 -20Q320 -22 298 -22Q199 -22 144 33L134 44L106 13Q83 -14 78 -18T65 -22Q52 -22 52 -14Q52 -11 110 221Q112 227 130 227H143Q149 221 149 216Q149 214 148 207T144 186T142 153Q144 114 160 87T203 47T255 29T308 24Z"></path></g><g data-mml-node="mi" transform="translate(3828.6,0)"><path data-c="1D462" d="M21 287Q21 295 30 318T55 370T99 420T158 442Q204 442 227 417T250 358Q250 340 216 246T182 105Q182 62 196 45T238 27T291 44T328 78L339 95Q341 99 377 247Q407 367 413 387T427 416Q444 431 463 431Q480 431 488 421T496 402L420 84Q419 79 419 68Q419 43 426 35T447 26Q469 29 482 57T512 145Q514 153 532 153Q551 153 551 144Q550 139 549 130T540 98T523 55T498 17T462 -8Q454 -10 438 -10Q372 -10 347 46Q345 45 336 36T318 21T296 6T267 -6T233 -11Q189 -11 155 7Q103 38 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(4400.6,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(4851.6,0)"><path data-c="1D463" d="M173 380Q173 405 154 405Q130 405 104 376T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Q21 294 29 316T53 368T97 419T160 441Q202 441 225 417T249 361Q249 344 246 335Q246 329 231 291T200 202T182 113Q182 86 187 69Q200 26 250 26Q287 26 319 60T369 139T398 222T409 277Q409 300 401 317T383 343T365 361T357 383Q357 405 376 424T417 443Q436 443 451 425T467 367Q467 340 455 284T418 159T347 40T241 -11Q177 -11 139 22Q102 54 102 117Q102 148 110 181T151 298Q173 362 173 380Z"></path></g><g data-mml-node="mi" transform="translate(5336.6,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(5681.6,0)"><path data-c="1D463" d="M173 380Q173 405 154 405Q130 405 104 376T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Q21 294 29 316T53 368T97 419T160 441Q202 441 225 417T249 361Q249 344 246 335Q246 329 231 291T200 202T182 113Q182 86 187 69Q200 26 250 26Q287 26 319 60T369 139T398 222T409 277Q409 300 401 317T383 343T365 361T357 383Q357 405 376 424T417 443Q436 443 451 425T467 367Q467 340 455 284T418 159T347 40T241 -11Q177 -11 139 22Q102 54 102 117Q102 148 110 181T151 298Q173 362 173 380Z"></path></g><g data-mml-node="mi" transform="translate(6166.6,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(6651.6,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(7102.6,0)"><path data-c="1D447" d="M40 437Q21 437 21 445Q21 450 37 501T71 602L88 651Q93 669 101 677H569H659Q691 677 697 676T704 667Q704 661 687 553T668 444Q668 437 649 437Q640 437 637 437T631 442L629 445Q629 451 635 490T641 551Q641 586 628 604T573 629Q568 630 515 631Q469 631 457 630T439 622Q438 621 368 343T298 60Q298 48 386 46Q418 46 427 45T436 36Q436 31 433 22Q429 4 424 1L422 0Q419 0 415 0Q410 0 363 1T228 2Q99 2 64 0H49Q43 6 43 9T45 27Q49 40 55 46H83H94Q174 46 189 55Q190 56 191 56Q196 59 201 76T241 233Q258 301 269 344Q339 619 339 625Q339 630 310 630H279Q212 630 191 624Q146 614 121 583T67 467Q60 445 57 441T43 437H40Z"></path></g><g data-mml-node="mi" transform="translate(7806.6,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mo" transform="translate(8569.3,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mi" transform="translate(9125.1,0)"><path data-c="1D446" d="M308 24Q367 24 416 76T466 197Q466 260 414 284Q308 311 278 321T236 341Q176 383 176 462Q176 523 208 573T273 648Q302 673 343 688T407 704H418H425Q521 704 564 640Q565 640 577 653T603 682T623 704Q624 704 627 704T632 705Q645 705 645 698T617 577T585 459T569 456Q549 456 549 465Q549 471 550 475Q550 478 551 494T553 520Q553 554 544 579T526 616T501 641Q465 662 419 662Q362 662 313 616T263 510Q263 480 278 458T319 427Q323 425 389 408T456 390Q490 379 522 342T554 242Q554 216 546 186Q541 164 528 137T492 78T426 18T332 -20Q320 -22 298 -22Q199 -22 144 33L134 44L106 13Q83 -14 78 -18T65 -22Q52 -22 52 -14Q52 -11 110 221Q112 227 130 227H143Q149 221 149 216Q149 214 148 207T144 186T142 153Q144 114 160 87T203 47T255 29T308 24Z"></path></g><g data-mml-node="mi" transform="translate(9770.1,0)"><path data-c="1D462" d="M21 287Q21 295 30 318T55 370T99 420T158 442Q204 442 227 417T250 358Q250 340 216 246T182 105Q182 62 196 45T238 27T291 44T328 78L339 95Q341 99 377 247Q407 367 413 387T427 416Q444 431 463 431Q480 431 488 421T496 402L420 84Q419 79 419 68Q419 43 426 35T447 26Q469 29 482 57T512 145Q514 153 532 153Q551 153 551 144Q550 139 549 130T540 98T523 55T498 17T462 -8Q454 -10 438 -10Q372 -10 347 46Q345 45 336 36T318 21T296 6T267 -6T233 -11Q189 -11 155 7Q103 38 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(10342.1,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(10793.1,0)"><path data-c="1D463" d="M173 380Q173 405 154 405Q130 405 104 376T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Q21 294 29 316T53 368T97 419T160 441Q202 441 225 417T249 361Q249 344 246 335Q246 329 231 291T200 202T182 113Q182 86 187 69Q200 26 250 26Q287 26 319 60T369 139T398 222T409 277Q409 300 401 317T383 343T365 361T357 383Q357 405 376 424T417 443Q436 443 451 425T467 367Q467 340 455 284T418 159T347 40T241 -11Q177 -11 139 22Q102 54 102 117Q102 148 110 181T151 298Q173 362 173 380Z"></path></g><g data-mml-node="mi" transform="translate(11278.1,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(11623.1,0)"><path data-c="1D463" d="M173 380Q173 405 154 405Q130 405 104 376T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Q21 294 29 316T53 368T97 419T160 441Q202 441 225 417T249 361Q249 344 246 335Q246 329 231 291T200 202T182 113Q182 86 187 69Q200 26 250 26Q287 26 319 60T369 139T398 222T409 277Q409 300 401 317T383 343T365 361T357 383Q357 405 376 424T417 443Q436 443 451 425T467 367Q467 340 455 284T418 159T347 40T241 -11Q177 -11 139 22Q102 54 102 117Q102 148 110 181T151 298Q173 362 173 380Z"></path></g><g data-mml-node="mi" transform="translate(12108.1,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(12593.1,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(13044.1,0)"><path data-c="1D439" d="M48 1Q31 1 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q146 66 215 342T285 622Q285 629 281 629Q273 632 228 634H197Q191 640 191 642T193 659Q197 676 203 680H742Q749 676 749 669Q749 664 736 557T722 447Q720 440 702 440H690Q683 445 683 453Q683 454 686 477T689 530Q689 560 682 579T663 610T626 626T575 633T503 634H480Q398 633 393 631Q388 629 386 623Q385 622 352 492L320 363H375Q378 363 398 363T426 364T448 367T472 374T489 386Q502 398 511 419T524 457T529 475Q532 480 548 480H560Q567 475 567 470Q567 467 536 339T502 207Q500 200 482 200H470Q463 206 463 212Q463 215 468 234T473 274Q473 303 453 310T364 317H309L277 190Q245 66 245 60Q245 46 334 46H359Q365 40 365 39T363 19Q359 6 353 0H336Q295 2 185 2Q120 2 86 2T48 1Z"></path></g><g data-mml-node="mi" transform="translate(13793.1,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(14244.1,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(14729.1,0)"><path data-c="1D45A" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(15884.9,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(16940.7,0)"><path data-c="38" d="M70 417T70 494T124 618T248 666Q319 666 374 624T429 515Q429 485 418 459T392 417T361 389T335 371T324 363L338 354Q352 344 366 334T382 323Q457 264 457 174Q457 95 399 37T249 -22Q159 -22 101 29T43 155Q43 263 172 335L154 348Q133 361 127 368Q70 417 70 494ZM286 386L292 390Q298 394 301 396T311 403T323 413T334 425T345 438T355 454T364 471T369 491T371 513Q371 556 342 586T275 624Q268 625 242 625Q201 625 165 599T128 534Q128 511 141 492T167 463T217 431Q224 426 228 424L286 386ZM250 21Q308 21 350 55T392 137Q392 154 387 169T375 194T353 216T330 234T301 253T274 270Q260 279 244 289T218 306L210 311Q204 311 181 294T133 239T107 157Q107 98 150 60T250 21Z"></path></g><g data-mml-node="mo" transform="translate(17718.4,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mn" transform="translate(18274.2,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(19052,0)"><path data-c="3A" d="M78 370Q78 394 95 412T138 430Q162 430 180 414T199 371Q199 346 182 328T139 310T96 327T78 370ZM78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z"></path></g><g data-mml-node="mn" transform="translate(19607.8,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g></svg></mjx-container></span>,如下图所示：</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230816115724431.png" alt=""><figcaption>image-20230816115724431</figcaption>
</figure>
<p>(6)<code>-XX:+UseParNewGC -XX:+UseConcMarkSweepGC</code>:垃圾回收器设置，<code>-XX:+UseParNewGC</code>表示设置新生代垃圾回收器为<code>ParNew</code>垃圾回收器。<code>-XX:+UseConcMarkSweepGC</code>表示设置老年代垃圾回收器为CMS垃圾回收器。</p>
<p>(7)OOM异常诊断设置：<code>-XX:+HeapDumpOnOutOfMemoryError</code>表示当发生OOM时转储到文件，<code>-XX:HeapDumpPath</code>表示堆的转储文件路径地址。这两个参数结合起来，可以在程序出现OOM时及时将对信息打印出来，方便后续分析故障。</p>
<h2 id="如何判断一个类是无用的类">如何判断一个类是无用的类？</h2>
<p>方法区主要回收的是无用的类，类需要同时满足下面3个条件才能算是“<strong>无用的类</strong>”：</p>
<ul>
<li>该类的所有实例都已经被回收，也就是Java堆中不存在该类的任何实例。</li>
<li>加载该类的<code>ClassLoader</code>已经被回收。</li>
<li>该类对应的<code>java.lang.Class</code>对象没有在任何地方被引用，无法在任何地方通过反射访问该类的方法。</li>
</ul>
<p>虚拟机可以对满足上述3个条件的无用类进行回收，这里说的仅仅是“可以”，而不是和对象一样不使用了就必然会被回收。</p>
<h2 id="在什么情况下可能会出现oom待补充">在什么情况下可能会出现OOM?(待补充)</h2>
<h1 id="mysql">MySQL</h1>
<h2 id="mysql数据类型">MySQL数据类型</h2>
<p><strong>数值类型</strong>：整型（tinyint、smallint、mediumint、int 和 bigint）、浮点型（float 和 double）、定点型（decimal）</p>
<p><strong>字符串类型</strong>：char、varchar、tinytext、text、mediumtext、longtext、tinyblob、blob、mediumblob 和 longblob 等，最常用的是 char 和 varchar 。</p>
<p><strong>日期时间类型</strong>：year、time、date、datetime 和 timestamp等。</p>
<p><img data-src="https://oss.javaguide.cn/github/javaguide/mysql/summary-of-mysql-field-types.png" alt="MySQL 常见字段类型总结" style="zoom: 50%;"></p>
<h3 id="decimal和floatdouble的区别是什么">decimal和float/double的区别是什么？</h3>
<p>decimal和float的区别是：decimal是定点数，float/double是浮点数，decimal可以存储精确的小数值，float/double只能存储近似的小数值。</p>
<p>decimal用于存储有精度要求的小数，比如与金钱相关的数据，可以避免浮点数带来的精度损失。</p>
<blockquote>
<p>在Java中，MySQL的decimal类型对应的是<code>java.math.BigDecilal</code>。</p>
</blockquote>
<h2 id="mysql常用的存储引擎">MySQL常用的存储引擎</h2>
<ul>
<li><strong>InnoDB</strong></li>
</ul>
<p><code>InnoDB</code>是MySQL的默认存储引擎，支持事务、行锁和外键等操作。InnoDB的底层存储结构为<code>B+</code>树，<code>B+</code>树的每个节点都对应InnoDB的一个Page,Page大小是固定的，一般被设置为16KB。其中非叶节点只有键值，叶节点包含完整的数据。</p>
<blockquote>
<p>适用场景：经常有数据更新的表，适合处理多重并发更新请求。</p>
<p>支持事务</p>
<p>支持灾难恢复(通过<code>binary log</code>日志恢复)</p>
<p>支持外键约束，只有<code>InnoDB</code>支持外键</p>
<p>支持自动增加列属性<code>auto_increment</code></p>
</blockquote>
<ul>
<li><p><strong>MyISAM</strong></p>
<p><code>MyISAM</code>不支持数据库事务、行锁和外键，因此在插入或更新数据时需要锁定整个表，效率较低。</p>
<p><code>MyISAM</code>的特性是执行读取操作的速度快，且占用的内存和存储资源较少。</p></li>
</ul>
<blockquote>
<p>总结：<code>MyISAM</code>的缺点是更新数据慢且不支持事务处理，优点是查询速度快。</p>
</blockquote>
<h2 id="数据库三范式">数据库三范式</h2>
<ul>
<li>1NF(第一范式)：确保每列的原子性，数据表的所有字段值都是不可分解的原子值。</li>
<li>2NF(第二范式)：在1NF的基础上，消除了非主属性对码的部分函数依赖(确保表中的每列都和主键相关)</li>
<li>3NF(第三范式)：满足1NF和2NF，消除了非主属性对码的的传递函数依赖(确保每列都和主键列直接相关而不是间接相关)。</li>
</ul>
<h2 id="索引">索引(*)</h2>
<p><strong>索引是一个单独的、存储在磁盘上的数据库结构，包含着对数据表中所有记录的引用指针</strong>。使用索引用于快速找出在某个或多个列中有一特定值的行，所有MySQL列类型都可以被索引，对相关列使用索引是提高查询操作速度的最佳途径。</p>
<h3 id="索引的优缺点">索引的优缺点</h3>
<p><strong>优点：</strong></p>
<ul>
<li><strong>大大加快数据检索的速度，这也是创建索引最主要的原因。</strong></li>
<li><strong>通过创建唯一索引，可以保证数据库表中每一行数据的唯一性。</strong></li>
<li><strong>加速表与表之间的连接。</strong></li>
<li><strong>在使用分组和排序子句进行数据查询时，可以显著减少查询中分组和排序的时间</strong>。</li>
</ul>
<blockquote>
<p><strong>缺点：</strong></p>
<ul>
<li>创建和维护索引要耗费时间</li>
<li>索引需要占用磁盘空间，除了数据表占用数据空间之外，每一个索引还要占一定的物理空间。</li>
<li>当对表中数据进行增加、删除和修改的时候，索引也要动态地维护</li>
</ul>
</blockquote>
<h3 id="mysql索引的类型有哪些数据结构维度">MySQL索引的类型有哪些(数据结构维度)？</h3>
<ul>
<li><code>FullText</code>:全文索引，一般用于查找文本中的关键字，而不是直接比较是否相等，可以在<code>CHAR、VARCHAR或Text</code>类型的列上创建，主要用来解决针对文本的模糊查询效率低的问题。MyISAM和InnoDB存储引擎在MySQL5.6.4以上版本支持全文索引。</li>
<li><code>HASH</code>:哈希索引，多用于等值查询，时间复杂度为O(1),效率非常高，但不支持排序、范围查询和模糊查询等。</li>
<li><code>BTree</code>:B+树索引，InnoDB存储引擎默认的索引，支持排序、分组、范围查询、模糊查询等，并且性能稳定。</li>
<li><code>RTree</code>:空间数据索引，多用于地理数据的存储，相比于其他索引，空间数据索引的优势在于范围查找。</li>
</ul>
<h3 id="常见的索引有哪些应用维度">常见的索引有哪些(应用维度)？</h3>
<ul>
<li><strong>普通索引</strong>:基本的索引类型，可以插入重复值和空值。</li>
<li><strong>主键索引</strong>：数据列不允许重复，不能为NULL，一张表只能有一个主键索引。</li>
<li><strong>组合索引</strong>：有多个列值组成的索引。</li>
<li><strong>唯一索引</strong>：数据列不允许重复，可以NUll,索引列的值必须唯一，如果是组合索引，则列值组合必须唯一。</li>
<li><strong>全文索引</strong>：一般用于查找文本中的关键字，而不是直接比较是否相等，可以在<code>CHAR、VARCHAR或Text</code>类型的列上创建，主要用来解决针对文本的模糊查询效率低的问题。MyISAM和InnoDB存储引擎在MySQL5.6.4以上版本支持全文索引。</li>
</ul>
<h3 id="聚簇索引和非聚簇索引的区别">聚簇索引和非聚簇索引的区别</h3>
<p><strong>(1)聚簇索引</strong>：将数据和索引放到一起存储，索引结构的叶子节点保留了数据行。</p>
<p><strong>优点：</strong></p>
<ul>
<li><strong>查询速度非常快</strong>：因为整个B+树本身就是一颗多叉平衡树，叶子节点也都是有序的，定位到索引的节点，就相当于定位到了数据。相比于非聚簇索引，聚簇索引少了一次读取数据的IO操作。</li>
<li><strong>对排序查找和范围查找优化</strong>：聚簇索引对于主键的排序查找和范围查找速度非常快。</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li><strong>依赖有序的数据</strong>：因为B+树是多路平衡树，如果索引的数据不是有序的，那么就需要在插入时排序，如果数据是整型还好，否则类似于字符串或UUID这种又长又难比较的数据，插入和查找的速度肯定比较慢。因此，对于InnoDB存储引擎，我们一般都会定义一个<strong>自增的ID列作为主键</strong>。</li>
<li><strong>更新主键的代价很高</strong>：因为将会导致被更新的行移动。因此，对于InnoDB存储引擎，我们一般定义<strong>主键为不可更新</strong>。</li>
</ul>
<p><strong>限制：</strong></p>
<ul>
<li>只有InnoDB引擎支持聚簇索引，<strong>MyISAM不支持聚簇索引</strong>。</li>
<li>由于数据的物理存储排序方式只能有一种，所以<strong>每个MySQL的表只能有一个聚簇索引</strong>。</li>
<li>如果没有为表定义主键，InnoDB会选择<strong>非空的唯一索引列代替</strong>。如果没有这样的列，InnoDB会<strong>隐式的定义一个主键</strong>作为聚簇索引。</li>
<li>为了充分利用聚簇索引的聚簇特性，InnoDB中表的<strong>主键应选择有序的id</strong>，不建议使用无序的id，比如UUID、MD5、HASH、字符串作为主键，无法保证数据的顺序增长。</li>
</ul>
<p><strong>(2)非聚簇索引</strong>：将数据和索引分开存储，索引叶子节点存储的是指向数据行的地址(主键id)。</p>
<blockquote>
<p>聚簇索引只能在搜索条件是主键值时才发挥作用，因为B+树中的数据都是按照主键进行排序的，如果我们想以别的列作为搜索条件，那么需要创建非聚簇索引。</p>
<p>非聚簇索引的叶子节点并不一定存放数据的指针，因为二级索引的叶子节点就存放的是主键，根据主键再回表查数据。</p>
</blockquote>
<blockquote>
<p>在<code>InnoDB</code>存储引擎中，默认的索引为B+树索引，利用主键创建的索引为主索引，也是聚簇索引，在主键之上创建的索引为辅助索引，也是非聚簇索引。</p>
<p>在<code>MyISAM</code>存储引擎中，默认的索引也是B+树索引，但主索引和辅助索引都是非聚簇索引，也就是说索引结构的叶子结点存储的是一个指向数据行的地址。并且使用辅助索引检索无需访问主键的索引。</p>
</blockquote>
<p><strong>例如，</strong><code>以c2列作为搜索条件</code>，那么需要使<code>用c2列创建一棵B+树</code>，如下所示：</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709130937991.png" alt=""><figcaption>image-20220709130937991</figcaption>
</figure>
<blockquote>
<p><strong>这个B+树与聚簇索引有几处不同</strong></p>
<ul>
<li><code>页内的记录</code>是按照从<code>c2列</code>的大小顺序排成一个<code>单向链表</code>。</li>
<li><code>页和页之间</code>也是根据页中记录的<code>c2列</code>的大小顺序排成一个<code>双向链表</code>。</li>
<li>非叶子节点存储的是记录的<code>c2列+页号</code>。</li>
<li>叶子节点存储的并不是完整的用户记录，而只是<code>c2列+主键</code>这两个列的值。</li>
</ul>
<p><strong>一张表可以有多个非聚簇索引：</strong></p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709134109900-16668534893372.png" alt="image-20220709134109900" style="zoom:80%;"></p>
</blockquote>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230730181803915.png" alt="image-20230730181803915" style="zoom: 80%;"></p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/mysql20210420165326946.png" style="zoom: 80%;"></p>
<h3 id="非聚簇索引一定会回表查询吗">非聚簇索引一定会回表查询吗？</h3>
<blockquote>
<p>回表查询：在非聚簇索引的叶子节点上存储的是主键，要先通过非聚簇索引找到主键，再通过聚簇索引找到主键所对应的数据(二次查询)。</p>
</blockquote>
<p>不一定，涉及到索引覆盖，如果查询的数据在辅助索引上完全能获取到就不需要回表查询。</p>
<p>(1)比如用户有id,name,age等字段，聚簇索引是以id为键值构建的索引，非聚簇索引是以name为键值构成的索引，</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select id,name from user where name='zhangsan'</span><br></pre></td></tr></table></figure>
<p>这种时候不需要回表查询，因为通过非聚簇索引已经能全部检索出数据，这就是索引覆盖的情况。</p>
<p>但是如果查询语句如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select id,name,age from user where name='zhangsan'</span><br></pre></td></tr></table></figure>
<p>这时就需要进行回表查询，因为通过非聚簇索引不能检索出age的值，解决办法是将索引覆盖即可，建立age和name的联合索引再使用<code>select id,name,age from user where name='zhangsan'</code>进行查询。</p>
<h3 id="innodb中的索引方案">InnoDB中的索引方案</h3>
<p>我们新分配一个编号为30的页来专门存储<strong>目录项记录</strong>，页10、28、9、20专门存储<strong>用户记录</strong>：</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709074801215.png" alt=""><figcaption>image-20220709074801215</figcaption>
</figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/1557565-20220429110413866-1755798300.png" alt=""><figcaption>img</figcaption>
</figure>
<p><strong>目录项记录和普通的用户记录的不同点：</strong></p>
<ul>
<li>目录项记录的<code>record_type</code>值是1，而 普通用户记录 的<code>record_type</code>值是0。</li>
<li>目录项记录只有主键值和页的编号两个列，而普通的用户记录的列是用户自己定义的，包含很多列，另外还有InnoDB自己添加的隐藏列。</li>
</ul>
<p><strong>现在查找主键值为 20 的记录，具体查找过程分两步：</strong></p>
<ol type="1">
<li>先到页30中通过二分法快速定位到对应目录项，因为<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.312ex;" xmlns="http://www.w3.org/2000/svg" width="13.953ex" height="1.819ex" role="img" focusable="false" viewBox="0 -666 6167.1 804"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mn"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z" transform="translate(500,0)"></path></g><g data-mml-node="mo" transform="translate(1277.8,0)"><path data-c="2264" d="M674 636Q682 636 688 630T694 615T687 601Q686 600 417 472L151 346L399 228Q687 92 691 87Q694 81 694 76Q694 58 676 56H670L382 192Q92 329 90 331Q83 336 83 348Q84 359 96 365Q104 369 382 500T665 634Q669 636 674 636ZM84 -118Q84 -108 99 -98H678Q694 -104 694 -118Q694 -130 679 -138H98Q84 -131 84 -118Z"></path></g><g data-mml-node="mn" transform="translate(2333.6,0)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z" transform="translate(500,0)"></path></g><g data-mml-node="mo" transform="translate(3611.3,0)"><path data-c="3C" d="M694 -11T694 -19T688 -33T678 -40Q671 -40 524 29T234 166L90 235Q83 240 83 250Q83 261 91 266Q664 540 678 540Q681 540 687 534T694 519T687 505Q686 504 417 376L151 250L417 124Q686 -4 687 -5Q694 -11 694 -19Z"></path></g><g data-mml-node="mn" transform="translate(4667.1,0)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z" transform="translate(500,0)"></path><path data-c="39" d="M352 287Q304 211 232 211Q154 211 104 270T44 396Q42 412 42 436V444Q42 537 111 606Q171 666 243 666Q245 666 249 666T257 665H261Q273 665 286 663T323 651T370 619T413 560Q456 472 456 334Q456 194 396 97Q361 41 312 10T208 -22Q147 -22 108 7T68 93T121 149Q143 149 158 135T173 96Q173 78 164 65T148 49T135 44L131 43Q131 41 138 37T164 27T206 22H212Q272 22 313 86Q352 142 352 280V287ZM244 248Q292 248 321 297T351 430Q351 508 343 542Q341 552 337 562T323 588T293 615T246 625Q208 625 181 598Q160 576 154 546T147 441Q147 358 152 329T172 282Q197 248 244 248Z" transform="translate(1000,0)"></path></g></g></g></svg></mjx-container></span>，就是页9。</li>
<li>再到页9中根据二分法快速定位到主键值为20的用户记录。</li>
</ol>
<p><strong>更复杂的情况如下：</strong></p>
<p>我们生成了一个存储更高级目录项的页33 这个页中的两条记录分别代表页30和页32，如果用户记录的主键值在<span class="math inline"><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="7.04ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 3111.7 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"></path></g><g data-mml-node="mn" transform="translate(278,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(778,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"></path></g><g data-mml-node="mn" transform="translate(1222.7,0)"><path data-c="33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z" transform="translate(500,0)"></path><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z" transform="translate(1000,0)"></path></g><g data-mml-node="mo" transform="translate(2722.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g></g></g></svg></mjx-container></span>之间，则到页30中查找更详细的目录项记录，如果主键值不小于320的话，就到页32中查找更详细的目录项记录。<strong>这个数据结构，它的名称是B+树 。</strong></p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20220709080648851.png" alt=""><figcaption>image-20220709080648851</figcaption>
</figure>
<h3 id="索引覆盖">索引覆盖</h3>
<p>如果一个索引包含(或者说覆盖)所有需要查询的字段，我们就称之为覆盖索引。</p>
<blockquote>
<p>在InnoDB存储引擎中，如果不是主键索引，叶子结点存储的是主键+列值，最终还是要<strong>回表</strong>，也就是要通过主键再查找一次，这样就比较慢。而覆盖索引就是要查询出的列和索引是对应的，不做回表操作。</p>
<p>如主键索引，如果一条SQL需要查询主键，那么正好根据主键索引就可以查到主键。再如普通索引，如果一条SQL需要查询name，name字段正好有索引，那么直接根据这个索引就可以查到数据，也无需回表。</p>
</blockquote>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/mysql20210420165341868.png" style="zoom:80%;"></p>
<h3 id="联合索引">联合索引</h3>
<p>使用表中的多个字段创建索引，就是<strong>联合索引</strong>，也叫<strong>组合索引</strong>或<strong>复合索引</strong>。</p>
<p>以<code>score</code>和<code>name</code>两个字段建立联合索引</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER TABLE `cus_order` ADD INDEX id_score_name(score, name);</span><br></pre></td></tr></table></figure>
<blockquote>
<p>最左前缀匹配原则：在使用联合索引时，MYSQL会根据联合索引中的字段顺序，从左到右依次到查询条件中去匹配，如果查询条件中存在与联合索引中最左侧字段相匹配的字段，则就会使用该字段过滤一批数据，直至联合索引中全部字段匹配完成，或者在执行过程中遇到范围查询(如<code>&gt;</code>、<code>&lt;</code>)才会停止匹配。对于<code>&gt;=</code>、<code>&lt;=</code>、<code>between</code>、<code>like</code>前缀匹配的范围查询，并不会停止匹配。</p>
</blockquote>
<h2 id="事务">事务</h2>
<blockquote>
<p>数据库事务是访问并可能操作各种数据项的一个数据库操作序列，这些操作要么全部执行，要么全部不执行，是一个不可分割的工作单位。</p>
</blockquote>
<h3 id="事务的特性">事务的特性</h3>
<ul>
<li>原子性：事务的操作要么全部执行成功，要么全部失败回滚。</li>
<li>一致性：事务在执行前后的状态是一样的。</li>
<li>隔离性：一个事务所进行的修改在最终提交之前，对其他事务是不可见的。</li>
<li>持久性：数据一单提交，其所做的操作将永久地保存到数据库中。</li>
</ul>
<h3 id="并发事务带来了哪些问题">并发事务带来了哪些问题？</h3>
<p><strong>(1)脏读</strong>：事务A更新了数据，但还没有提交，这时事务B读取到事务A更新后的数据，然后事务A回滚了，事务B读取到的数据就称为脏数据了。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155155628.png" alt=""><figcaption>image-20230801155155628</figcaption>
</figure>
<p><strong>(2)不可重复读</strong>：事务A对数据进行多次读取，事务B在事务A多次读取的过程中执行了更新操作并提交了，导致事务A多次读取到的数据不一致。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155454797.png" alt=""><figcaption>image-20230801155454797</figcaption>
</figure>
<p><strong>(3)幻读</strong>：事务A在读取数据后，事务B向事务A读取的数据中插入了几条数据，事务A再次读取数据时发现多了几条数据，和之前读取的数据不一致。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155747152.png" alt=""><figcaption>image-20230801155747152</figcaption>
</figure>
<p><strong>(4)丢失修改</strong>：事务A和事务B都对同一个数据进行修改，事务A先修改，事务B后修改，事务B的修改覆盖了事务A的修改。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801155910501.png" alt=""><figcaption>image-20230801155910501</figcaption>
</figure>
<blockquote>
<p><strong>不可重复度和幻读的区别</strong>：在不可重复读中，发现数据的不一致主要是数据被更新了。在幻读中，发现数据不一致主要是数据增多或者减少了。</p>
</blockquote>
<h3 id="数据库的隔离级别有哪些">数据库的隔离级别有哪些？</h3>
<ul>
<li><strong>读未提交(Read Uncommitted)</strong>：最低的隔离级别，允许读取尚未提交的数据变更，可能导致脏读、幻读和不可重复读。</li>
<li><strong>读已提交(Read Committed)</strong>：允许读取并发事务已经提交的数据，可以阻止脏读，但可能导致幻读和不可重复读。</li>
<li><strong>可重复读(Repeatable Read)</strong>：对同一字段的多次读取结果是一致的，除非数据是被本身事务自己所修改，可以阻止脏读和不可重复读，可能导致幻读。</li>
<li><strong>可串行化(Serializable)</strong>：最高的隔离级别，完全服从ACID的隔离级别，所有的事务依次逐个执行，这样事务之间就不会产生干扰，该级别可以防止脏读、不可重复读和幻读。</li>
</ul>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801163556062.png" alt=""><figcaption>image-20230801163556062</figcaption>
</figure>
<h3 id="隔离级别是如何实现的">隔离级别是如何实现的？</h3>
<p>事务的隔离级别主要是依靠锁机制和<code>MVCC</code>(多版本并发控制)实现的，提交读和可重复读可以通过MVCC实现，串行化可以通过锁机制实现。</p>
<blockquote>
<p>MySQL InnoDB存储引擎的默认隔离级别是<code>REPEATABLE-READ(可重复读)</code>。</p>
</blockquote>
<h3 id="mvcc待补充">MVCC(待补充)?</h3>
<p>MVCC(multiple version concurrent control，多版本并发控制)是一种控制并发的方法，主要用来提高数据库的并发性能。</p>
<blockquote>
<p>前置知识</p>
<p><strong>当前读</strong>：读取的是数据库的最新版本，并且在读取时要保证其他事务不会修改当前记录，所以会对读取的记录加锁。</p>
<p><strong>快照读</strong>：不加锁读取操作即为快照读，使用MVCC来读取快照中的数据，避免加锁带来的性能损耗。</p>
</blockquote>
<p><strong>MVCC的作用就是在不加锁的情况下，解决数据库读写冲突问题，并且解决脏读、幻读、不可重复读等问题，但是不能解决丢失修改问题</strong>。</p>
<p><strong>MVCC的实现原理</strong></p>
<ul>
<li><p><strong>版本号</strong></p>
<p>系统版本号：是一个自增的ID,每开启一个事务，系统版本号都会递增。</p>
<p>事务版本号：事务版本号就是事务开始时的系统版本号，可以通过事务版本号的大小判断事务的时间顺序。</p></li>
<li><p><strong>行记录隐藏的列</strong></p>
<p><code>DB_ROW_ID</code>(6字节):隐含的自增ID，如果没有设置主键且该表没有唯一非空索引时，<code>InnoDB</code>会使用该id来生成聚簇索引。</p>
<p><code>DB_TRX_ID(6字节)</code>:最近修改的事务ID,记录创建这条记录或最后一次修改这条记录的事务ID。</p>
<p><code>DB_ROLL_PTR(7字节)</code>:回滚指针，指向这条记录的上一个版本。</p>
<p>大致如下图(省略了具体字段的值)：</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230801172557926.png" alt=""><figcaption>image-20230801172557926</figcaption>
</figure></li>
<li><p><strong>undo日志</strong>:</p></li>
</ul>
<p>​ MVCC将使用到的快照会存储在undo日志中，该日志通过回滚指针将一个个数据行的所有快照连接起来。大致如下图：</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161759827.png" style="zoom:80%;"></p>
<p>举一个简单的例子说明下，比如最开始的某条记录长这样：</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161801004.png" style="zoom:80%;"></p>
<p>现在来了一个事务对他的年龄字段进行了修改，变成了这样：</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161802538.png" style="zoom:80%;"></p>
<p>现在又来了一个事务2对他的性别进行了修改，又变成了这样：</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204161803933.png" style="zoom:80%;"></p>
<p>从上面的分析可以看出，事务对同一记录的修改，记录的各个日志条目会在Undo日志中连接成一个线性表，在表头的就是最新的旧记录。</p>
<blockquote>
<p>在重复读的隔离级别下，<code>InnoDB</code>的工作流程：</p>
<ul>
<li><p><code>SELECT</code></p>
<p>作为查询的结果要满足两个条件：</p>
<p>(1)当前事务所要查询的数据行快照的创建版本必须小于当前事务的版本号，这样做的目的是保证当前事务读取的数据行的快照要么是在当前事务开始前就已经存在的，要么就是当前事务自身插入或者修改过的。</p>
<p>(2)当前事务所要读取的数据行快照的删除版本号必须是大于当前事务的版本号，如果是小于等于的话，表示该数据行已经被删除，不能读取。</p></li>
<li><p><code>INSERT</code>:将当前系统版本号作为数据行快照的创建版本号。</p></li>
<li><p><code>DELETE</code>:将当前系统版本号作为数据行快照的删除版本号。</p></li>
<li><p><code>UPDATE</code>:保存当前系统版本号为更新前的数据行快照创建行版本号，并保存当前系统版本号为更新后的数据行快照的删除版本号，其实就是，先删除再插入即为更新。</p></li>
</ul>
</blockquote>
<p>总结：MVCC的作用就是在避免加锁的情况下最大限度解决读写并发冲突地问题，它可以实现读已提交和可重复读两个隔离级别。</p>
<h2 id="数据库锁">数据库锁</h2>
<blockquote>
<p>当数据库有并发事务的时候，保证数据访问顺序的机制被称为锁机制。</p>
</blockquote>
<h3 id="数据库锁的类型有哪些">数据库锁的类型有哪些？</h3>
<blockquote>
<ul>
<li>行锁</li>
<li>表锁</li>
<li>页级锁</li>
<li>基于Redis的分布式锁</li>
</ul>
</blockquote>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802143255849.png" alt=""><figcaption>image-20230802143255849</figcaption>
</figure>
<p>MyISAM仅支持表级锁，一锁就锁整张表，这在并发写的情况下性能非常差。</p>
<p>InnoDB支持表级锁和行级锁，默认是行级锁。</p>
<p>行级锁的粒度更小，仅对相关的记录上锁即可(对一行或者多行记录加锁)，所以对于并发写入操作来说，InnoDB的性能更高。</p>
<p>从锁的类别上区别可以分为共享锁和排他锁。</p>
<ul>
<li><strong>共享锁(S锁)</strong>：又称<strong>读锁</strong>，事务在读取记录的时候获取共享锁，允许多个事务同时获取(锁兼容)。</li>
<li><strong>排它锁(X锁)</strong>：又称<strong>写锁/独占锁</strong>，事务正在修改记录的时候获取排它锁，不允许多个事务同时获取。如果一个记录已经加了排它锁，那其他事务不能再对这条事务加任何类型的锁(锁不相兼容)。</li>
</ul>
<blockquote>
<p>排它锁与任何的锁都不兼容，共享锁仅和共享锁兼容。</p>
</blockquote>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802144826618.png" alt=""><figcaption>image-20230802144826618</figcaption>
</figure>
<p>由于MVCC的存在，对于一般的<code>SELECT</code>语句，InnoDB不会加任何锁。不过可以通过如下语句加锁:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># 共享锁 可以在MySQL5.7和MySQL8.0中使用</span><br><span class="line">SELECT ... LOCK IN SHARE MODE;</span><br><span class="line"># 共享锁 可以在MySQL8.0中使用</span><br><span class="line">SELECT ... FOR SHARE;</span><br><span class="line"># 排他锁</span><br><span class="line">SELECT ... FOR UPDATE;</span><br></pre></td></tr></table></figure>
<h3 id="innodb有哪几类行锁">InnoDB有哪几类行锁？</h3>
<p><strong>InnoDB行锁是通过对索引数据页上的记录加锁实现的</strong>，MySQL InnoDB支持三种行锁定方式：</p>
<ul>
<li><strong>记录锁(Record Lock)</strong>:属于单个行记录上的锁。</li>
<li><strong>间隙锁(Gap Lock)</strong>:锁定一个范围，不包括记录本身。</li>
<li><strong>临键锁(Next-Key Lock)</strong>:Record Lock+Gap Lock,锁定一个范围，包括记录本身，主要目的是为了解决幻读问题。记录锁只能锁住已经存在的记录，为了便面插入新记录，需要依赖间隙锁。</li>
</ul>
<blockquote>
<p>在InnoDB默认的隔离级别 <code>REPEATABLE-READ</code>下，行锁默认使用的是<code>Next-Key Lock</code>。但是，如果操作的索引是唯一索引或主键，InnoDB 会对Next-Key Lock进行优化，将其降级为Record Lock，即仅锁住索引本身，而不是范围。</p>
</blockquote>
<h3 id="什么是数据库的乐观锁和悲观锁如何实现">什么是数据库的乐观锁和悲观锁？如何实现？</h3>
<p><strong>乐观锁</strong>：系统假设数据的更新大多数时候是不会产生冲突的，所以数据库只在更新操作提交的时候对数据监测冲突，如果存在冲突，则数据更新失败。</p>
<p>乐观锁实现方式：一般通过<strong>版本号</strong>和<strong>CAS算法</strong>实现。</p>
<p><strong>悲观锁</strong>：假定会发生并发冲突，屏蔽一切可能违反数据完整性的操作。通俗讲就是每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会加锁。</p>
<p>悲观锁的实现方式：通过数据库的锁机制实现，对查询语句添加<code>for update</code>。</p>
<h2 id="mysql日志待补充">MySQL日志(待补充)</h2>
<h2 id="mysql基础知识待补充">MySQL基础知识(待补充)</h2>
<h3 id="mysql的几种连接查询待完善">MySQL的几种连接查询(待完善)</h3>
<p>(1)外连接：主要分为左外连接(LEFT JOIN)、右外连接(RIGHT JOIN)</p>
<p>左外连接：显示左表中农所有的数据及右表中符合条件的数据，右表不符合条件的数据为null。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802155413278.png" alt=""><figcaption>image-20230802155413278</figcaption>
</figure>
<p>右外连接：显示右表中所有的数据及左表中符合条件的数据，左表不符合条件的数据为null</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802155810213.png" alt=""><figcaption>image-20230802155810213</figcaption>
</figure>
<p>(2)内连接：只显示符合条件的数据</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802155842162.png" alt=""><figcaption>image-20230802155842162</figcaption>
</figure>
<h2 id="数据库优化">数据库优化</h2>
<h3 id="大表如何优化">大表如何优化？</h3>
<ul>
<li>限定数据的范围：避免不带任何限制数据范围条件的查询语句。</li>
<li>读写分离：主库负责写，从库负责读</li>
<li>垂直分表：将一个表按照字段分成多个表，每个表存储其中一部分字段。</li>
<li>水平分表：在同一个数据库内，把一个表的数据按照一定规则拆分到多个表中。</li>
<li>对单表进行优化：对表中的字段、索引、查询SQL进行优化。</li>
<li>添加缓存。</li>
</ul>
<h3 id="什么是分库">什么是分库？</h3>
<p><strong>分库</strong>就是将数据库中的数据分散到不同的数据库上，可以垂直分库，也可以水平分库。</p>
<p><strong>垂直分库</strong>就是把单一数据库按照业务进行划分，不同的业务使用不同的数据库，进而将一个数据库的压力分担到多个数据库。</p>
<p>举个例子：说你将数据库中的用户表、订单表和商品表分别单独拆分为用户数据库、订单数据库和商品数据库。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802162859281.png" alt=""><figcaption>image-20230802162859281</figcaption>
</figure>
<p><strong>水平分库</strong>是把同一个表按一定规则拆分到不同的数据库中，每个库可以位于不同的服务器上，这样就实现了水平扩展，解决了单表的存储和性能瓶颈的问题。</p>
<p>举个例子：订单表数据量太大，你对订单表进行了水平切分(水平分表)，然后将切分后的2张订单表分别放在两个不同的数据库。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802163930423.png" alt=""><figcaption>image-20230802163930423</figcaption>
</figure>
<h3 id="什么是分表">什么是分表？</h3>
<p><strong>分表</strong>就是对单表的数据进行拆分，可以是垂直拆分，也可以是水平拆分。</p>
<p><strong>垂直分表</strong>是对数据表列的拆分，把一张列比较多的表拆分为多张表。</p>
<p>举个例子：我们可以将用户信息表中的一些列单独抽出来作为一个表。</p>
<p><strong>水平分表</strong>是对数据表行的拆分，把一张行比较多的表拆分为多张表，可以解决单一表数据量过大的问题。</p>
<p>举个例子：我们可以将用户信息表拆分成多个用户信息表，这样就可以避免单一表数据量过大对性能造成影响。</p>
<p>水平拆分只能解决单表数据量大的问题，为了提升性能，我们通常会选择将拆分后的多张表放在不同的数据库中。也就是说，水平分表通常和水平分库同时出现。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802164022191.png" alt=""><figcaption>image-20230802164022191</figcaption>
</figure>
<h3 id="什么情况下需要分库分表">什么情况下需要分库分表？</h3>
<ul>
<li>单表的数据达到千万级别以上，数据库读写速度比较缓慢。</li>
<li>数据库中的数据占用的空间越来越大，备份时间越来越长。</li>
<li>应用的并发量太大。</li>
</ul>
<h3 id="分库分表后id键如何处理">分库分表后，ID键如何处理？</h3>
<p>分库分表后不能每个表的ID都从1开始，需要一个全局ID，设置全局ID主要有以下几种方法：</p>
<ul>
<li><p>UUID:本地生成ID，不需要远程调用；全局唯一不重复。缺点是占用空间大，不适合作为索引。</p></li>
<li><p>自增ID：在分库分表后使用数据库自增ID，需要一个专门用于生成主键的库，每次服务接收到请求，先向这个库中插入一条没有意义的数据，获取一个数据库自增的ID，利用这个ID去分库分表中写数据。</p>
<p>优点：简单易实现。缺点：在高并发下存在瓶颈。</p></li>
</ul>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/img202204222219378.PNG" style="zoom:80%;"></p>
<ul>
<li><p>Redis生成ID:不依赖数据库，性能比较好。缺点：引入新组件会使系统复杂度增加。</p></li>
<li><p>Twitter的snowflake算法：是一个64位的long类型ID，其中有1bit是不用的，41bit作为毫秒数，10bit作为工作机器ID,12bit作为序列号。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802165134303.png" alt=""><figcaption>image-20230802165134303</figcaption>
</figure></li>
<li><p>美团的Leaf分布式ID生成系统：https://tech.meituan.com/2017/04/21/mt-leaf.html</p></li>
</ul>
<h3 id="mysql主从复制原理">MySQL主从复制原理</h3>
<p>MySQL复制：为保证主服务器和从服务器的数据一致性，在向主服务器插入数据后，从服务器会自动将主服务器中修改的数据同步过来。</p>
<p><strong>主从复制的原理</strong></p>
<p>主从复制主要有三个线程：<code>binlog</code>线程、<code>I/O</code>线程、<code>SQL</code>线程。</p>
<ul>
<li>binlog线程：负责将主服务器上的数据更改写入到二进制日志(<code>Binary log</code>)中。</li>
<li>I/O线程：负责从主服务器上读取二进制日志(Binary log),并写入从服务器的中继日志(Relay log)中</li>
<li>SQL线程：负责读取中继日志，解析出主服务器中已经执行的数据更改并在从服务器中重放</li>
</ul>
<p>复制过程如下：</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802171749497.png" alt=""><figcaption>image-20230802171749497</figcaption>
</figure>
<p>(1)Master在每个事务更新数据完成之前，将操作记录写入到binlog中。</p>
<p>(2)Slave从库连接Master主库，并且Master有多少个Slave就会创建多少个<code>binlog dump</code>线程。当Master结点的binlog发生变化时，binlog dump会通知所有的Slave，并将相应的binlog发送给Slave。</p>
<p>(3)I/O线程接收到binlog内容后，将其写入到中继日志(<code>Relay log</code>)中。</p>
<p>(4)SQL线程读取中继日志，并在从服务器中重放。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802172421442.png" alt=""><figcaption>image-20230802172421442</figcaption>
</figure>
<h3 id="什么是读写分离">什么是读写分离？</h3>
<p><strong>读写分离主要是为了将对数据库的读写操作分散到不同的数据库节点上</strong>，能否小幅提升写性能，大幅提升读性能。</p>
<p><strong>读写分离主要依赖于主从复制，主从复制为读写分离服务</strong>。</p>
<p>读写分离的优势：</p>
<ul>
<li>主服务器负责写，从服务器负责读，缓解了锁的竞争。</li>
<li>从服务器可以使用MyISAM，提升查询性能及节约系统开销。</li>
<li>增加冗余，提高可用性。</li>
</ul>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230802172736490.png" alt=""><figcaption>image-20230802172736490</figcaption>
</figure>
<h1 id="redis">Redis</h1>
<h2 id="什么是redis">什么是Redis?</h2>
<p>Redis是一个高性能的非关系型的键值对数据库，使用C编写实现的。与传统的数据库不同的是Redis是存在内存中的，所以读写速度非常快，每秒可以处理超过<strong>10万次</strong>的读写操作，这也是Redis常常被用作<strong>缓存</strong>的原因。</p>
<h2 id="redis的优缺点">Redis的优缺点？</h2>
<p>优点：</p>
<ul>
<li><strong>读写性能好</strong>，读的速度可达110000次/秒，写的速度可达81000次/秒。</li>
<li><strong>支持数据持久化</strong>，有AOF和RDB两种持久化方式。</li>
<li><strong>数据结构丰富</strong>，支持String、List、Set、Hash等结构</li>
<li><strong>支持事务</strong>，Redis所有的操作都是原子性的，并且还支持几个操作合并后的原子性执行，原子性指操作要么全部成功执行，要么失败不执行。</li>
<li><strong>支持主从复制</strong>，主库可以自动将数据同步到从库，进行读写分离。</li>
</ul>
<p>缺点：</p>
<ul>
<li>因为Redis是将数据存到内存中，所以会受到内存大小的限制，不能用作海量数据的读写。</li>
<li>Redis不具备自动容错和恢复功能，主机或从机宕机会导致前端部分读写请求失败。</li>
</ul>
<h2 id="redis单线程快的原因">Redis单线程快的原因</h2>
<ul>
<li>纯内存操作</li>
<li>基于非阻塞的IO多路复用机制</li>
<li>采用单线程避免了多线程的频繁上下文切换带来的性能问题</li>
</ul>
<h2 id="redis的应用场景">Redis的应用场景</h2>
<ul>
<li>缓存：Redis基于内存，读写速度非常快，并且有键过期功能和键淘汰策略，可以作为缓存使用。</li>
<li>排行榜：Redis提供的有序集合可以很方便地实现排行榜。</li>
<li>分布式锁：Redis的<code>SETNX</code>命令来实现分布式锁。</li>
<li>社交功能：实现共同好友、共同关注等。</li>
<li>计数器：通过String进行自增自减实现计数功能。</li>
<li>消息队列：Redis提供了发布、订阅、阻塞队列等功能，可以实现一个简单的消息队列。</li>
</ul>
<h2 id="redis是单线程的如何提高cpu的利用率">Redis是单线程的，如何提高CPU的利用率</h2>
<p>可以在一个服务器上部署多个Redis实例，把它们当作不同的服务器使用。</p>
<h2 id="redis的数据类型有哪些">Redis的数据类型有哪些?</h2>
<ul>
<li><code>String</code>(字符串)</li>
<li><code>Hash</code>(哈希)</li>
<li><code>List</code>(列表)</li>
<li><code>Set</code>(集合)</li>
<li><code>ZSet</code>(有序集合)</li>
<li><code>Bitmap</code>(位图)</li>
<li><code>HyperLogLog</code>(超级日志)</li>
<li><code>Geospatial</code>(地理空间)</li>
</ul>
<h3 id="zset的底层数据结构是什么跳表了解吗">ZSet的底层数据结构是什么？跳表了解吗？</h3>
<p><strong>skipList(跳表)</strong>首先是链表，但与传统链表相比有几点差异：</p>
<ul>
<li>元素按照升序排列存储</li>
<li>节点可能包含多个指针，指针跨度不同。</li>
</ul>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808152038722.png" alt=""><figcaption>image-20230808152038722</figcaption>
</figure>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808153322054.png" alt=""><figcaption>image-20230808153322054</figcaption>
</figure>
<p><strong>SkipList的特点：</strong></p>
<ul>
<li>跳表是一个双向链表，每个节点都包含score和ele值</li>
<li>节点按照score值排序,score值一样则按照ele字典排序</li>
<li>每个节点都可以包含多层指针，层数是1到32之间的随机数</li>
<li>不同层指针到下一个节点的跨度不同，层级越高，跨度越大</li>
<li>增删改查效率与红黑树一致，实现却更简单</li>
</ul>
<h2 id="redis键过期的删除策略">Redis键过期的删除策略</h2>
<h3 id="键的过期删除策略">键的过期删除策略</h3>
<p>常见的过期删除策略是<strong>惰性删除</strong>、<strong>定期删除</strong>、<strong>定时删除</strong>。</p>
<ul>
<li><p><strong>惰性删除</strong>：只有访问这个键时才会检查它是否过期，如果过期则清除。</p>
<p>优点：最大化的节约CPU资源。</p>
<p>缺点：如果大量过期键没有被访问，会一致占用大量内存。</p></li>
<li><p><strong>定时删除</strong>：为每个设置过期时间的key都创造一个定时器，到了过期时间就清除。</p>
<p>优点：可以立即清除过期的键。</p>
<p>缺点：会占用大量的CPU资源去处理过期的数据。</p></li>
<li><p><strong>定期删除</strong>：每隔一段时间就对一些键进行检查，删除其中过期的键。该策略是惰性删除和定时删除的一个折中，既避免了占用大量CPU资源又避免了出现大量过期键不被清除占用内存的情况。</p></li>
</ul>
<p>Redis中同时使用了<strong>惰性删除</strong>和<strong>定期删除</strong>两种。</p>
<h3 id="redis的内存淘汰机制">Redis的内存淘汰机制？</h3>
<p>当存入的数据超过Redis最大允许内存后，会触发Redis的内存淘汰策略。在Redis4.0前一共有6种淘汰策略。</p>
<ul>
<li>volatile-lru:当Redis内存不足时，会在设置了过期时间的键中使用LRU算法移除最近最少使用的键。</li>
<li>volatile-ttl:从设置了过期时间的键中移除要将过期的。</li>
<li>volatile-random:从设置了过期时间的键中随机淘汰一些。</li>
<li>allkeys-lru:根据LRU算法移除一些键。</li>
<li>allkeys-random:随机移除某些键。</li>
<li>noeviction:新的写入操作会报错。</li>
</ul>
<blockquote>
<p>前三个是在设置了<strong>过期时间</strong>的键的空间进行移除，后三个是在全局的空间进行移除。</p>
</blockquote>
<p>4.0版本后新增以下两种：</p>
<ul>
<li>volatile-lfu:从设置过期时间的键中移除一些最不经常使用的键(Least Frequently Used,LFU)</li>
<li>allkeys-lfu:从所有键中移除一些最不经常使用的键。</li>
</ul>
<h2 id="redis持久化策略以及优缺点">Redis持久化策略以及优缺点</h2>
<p>Redis是基于内存的，为了防止一些意外情况导致数据丢失，需要将数据持久化到磁盘上。</p>
<p>Redis提供了两种持久化方式，一种是<strong>RDB</strong>，一种是<strong>AOF</strong>。</p>
<h3 id="rdbredis-database">(1)RDB(Redis Database)</h3>
<p>RDB是Redis的默认持久化方式，按照一定的时间间隔将内存的数据以快照的形式保存到磁盘，恢复时将快照读取到内存中。RDB持久化实际操作过程是fork一个子进程，先将数据集写入临时文件，写入成功后，再替换之前的文件，用二进制压缩存储，如下图。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817104857287.png" alt=""><figcaption>image-20230817104857287</figcaption>
</figure>
<p><strong>优点：</strong></p>
<ul>
<li>适合对大规模的数据恢复，比AOF的启动效率高</li>
<li>只有一个文件<code>dump.rdb</code>，方便持久化</li>
<li>性能最大化，再开始持久化时，它唯一需要做的只是fork出子进程，之后再由子进程完成这些持久化的工作，这样就可以极大的避免服务进程执行IO操作了。</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>数据安全性低，在一定间隔时间内做一次备份，如果Redis突然宕机，会丢失最后依次快照的修改</li>
<li>由于RDB是通过fork子进程来协助完成数据持久化工作的，因此当数据集较大时，可能会导致整个服务器停止服务几百毫秒，甚至是1秒钟。</li>
</ul>
<h3 id="aofappend-only-file">(2)AOF(Append-only File)</h3>
<p><code>AOF</code>持久化以日志的形式记录服务器所处理的每一个写、删除操作，查询操作不会记录，以文本的方式记录，可以打开文件看到详细的操作记录。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817105249355.png" alt=""><figcaption>image-20230817105249355</figcaption>
</figure>
<p><strong>优点：</strong></p>
<ul>
<li>具备更高的安全性，Redis提供了3种同步策略，分别是每秒同步、每修改同步和不同步。相比RDB突然宕机丢失的数据会更少，每秒同步会丢失一秒钟的数据，每修改同步不会丢失数据。</li>
<li>由于该机制对日志文件的写入操作采用的是append模式，因此在写入过程中即使出现宕机现象，也不会破坏日志文件中已经存在的内容。</li>
<li>AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作，可以通过该文件完成数据的重建。</li>
</ul>
<p><strong>缺点：</strong></p>
<ul>
<li>对于相同数量的数据集而言，AOF文件通常要大于RDB文件。RDB在恢复大数据集时的速度比AOF快。</li>
<li>根据AOF选择同步策略的不同，效率也不同，但AOF在运行效率上往往会慢于RDB。</li>
</ul>
<h2 id="redis的事务">Redis的事务</h2>
<h3 id="什么是redis的事务">什么是Redis的事务</h3>
<p><strong>Redis</strong>的事务是一个单独的隔离操作，事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中，不会被其他客户端发送来的命令请求所打断，所以Redis事务是在一个队列中，一次性、顺序性、排他性地执行一系列命令。</p>
<p>Redis事务的主要作用就是<strong>串联多个命令防止别的命令插队</strong>。</p>
<h3 id="redis事务的相关命令">Redis事务的相关命令</h3>
<ul>
<li><p><code>MULTI</code>：用于标记一个事务块的开始。</p></li>
<li><p><code>EXEC</code>：执行事务队列内的所有命令。</p></li>
<li><p><code>DISCARD</code>：命令取消事务，放弃执行事务队列内的所有命令，恢复连接为非(transaction)模式，如果正在使用 WATCH命令监视某个(或某些)key，那么取消所有监视，等同于执行命令UNWATCH。</p></li>
<li><p><code>WATCH</code>：用于标记要监视的key，以便有条件地执行事务，WATCH命令可以监控一个或多个键，一旦其中有一个键被修改（或删除），之后的事务就不会执行。</p></li>
<li><p><code>UNWATCH</code>：用于取消WATCH命令对所有key的监视。如果已经执行过了EXEC或DISCARD命令，则无需再执行UNWATCH命令，因为执行EXEC命令时，开始执行事务，WATCH命令也会生效，而DISCARD命令在取消事务的同时也会取消所有对 key的监视，所以不需要再执行UNWATCH命令了</p>
<p><a target="_blank" rel="noopener" href="https://codeleader.blog.csdn.net/article/details/120375082">Redis事务操作</a></p></li>
</ul>
<h3 id="redis事务执行的三个阶段">Redis事务执行的三个阶段</h3>
<ul>
<li>开始事务(MULTI)</li>
<li>命令入队列</li>
<li>执行事务(EXEC)</li>
</ul>
<h3 id="redis事务的特性">Redis事务的特性</h3>
<p><strong>Redis事务不保证原子性</strong>，单条的Redis命令是原子性的，但事务不能保证原子性。</p>
<p><strong>Redis事务是有隔离性的</strong>，但没有隔离级别，事务中的所有命令都会序列化、按顺序的执行。事务在执行的过程中，不会被其他客户端发送来的命令请求所打断。(顺序性、排他性)</p>
<p><strong>Redis事务不支持回滚</strong>，Redis执行过程中的命令执行失败，其他命令仍然可以执行。(一次性)</p>
<h2 id="redis集群">Redis集群</h2>
<h3 id="redis集群的实现方案有哪些">Redis集群的实现方案有哪些？</h3>
<ul>
<li><p>Redis主从模式</p></li>
<li><p>Redis哨兵模式</p></li>
<li><p>Redis Cluster</p></li>
</ul>
<h3 id="redis的主从复制机制">Redis的主从复制机制</h3>
<blockquote>
<p>一个主库可以拥有多个备库，而一个备库只能拥有一个主库。</p>
<p>Redis的主从同步分为<strong>增量同步</strong>和<strong>全量同步</strong>。</p>
</blockquote>
<ul>
<li>一个备库在启动后，会向主库发送<code>SYNC</code>数据同步命令。</li>
<li>主库在接收到<code>SYNC</code>命令后会开始在后台保存快照(即<code>RDB</code>持久化)，并保存在快照期间接收到的命令。在该持久化过程中会生成一个<code>.rdb</code>快照文件。</li>
<li>在主库快照执行完成后，Redis会将快照文件和所有缓存的命令以<code>.rdb</code>快照文件的形式发送给备库。</li>
<li>备库在收到主库的<code>.rdb</code>快照文件后，会将快照文件保存到本地。</li>
<li>备库载入<code>.rdb</code>快照文件的数据到内存。以上过程被称为复制初始化(这时候是<strong>全量复制</strong>)。</li>
<li>在复制初始化结束后，主库在每次收到写请求命令时都会将该命令同步给备库，从而保证主备数据库的数据一致(此时是<strong>增量复制</strong>)。</li>
</ul>
<h3 id="redis主从集群">Redis主从集群</h3>
<p>所有的写请求都被发送到主库，再由主库将数据同步到备库。主库主要执行写操作和数据同步，备库主要执行读操作缓解系统压力。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804145343691.png" alt=""><figcaption>image-20230804145343691</figcaption>
</figure>
<blockquote>
<p>一个主库可以有多个备库，备库还可以作为其他数据库的主库。</p>
</blockquote>
<p>优点：</p>
<ul>
<li>高可靠性，在master库出现故障后，可以切换到slave库</li>
<li>读写分离，slave库可以扩展master库节点的读能力，有效应对大并发量的读操作</li>
</ul>
<p>缺点：</p>
<ul>
<li>不具备自动容错和恢复能力，主节点故障，从节点需要手动升级为主节点，可用性较低</li>
</ul>
<h3 id="redis哨兵集群">Redis哨兵集群</h3>
<p>在主备模式上添加了一个哨兵的角色来监控集群的运行状态。哨兵通过发送命令让Redis服务器返回其运行状态。哨兵是一个独立运行的进程，在检测到<code>Master</code>宕机时会自动将<code>Slave</code>切换为<code>Master</code>，然后通过发布与订阅模式通知其他从服务器修改配置文件，完成主备热切。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804145930965.png" alt=""><figcaption>image-20230804145930965</figcaption>
</figure>
<p><strong>哨兵集群的主要作用</strong>如下：</p>
<ul>
<li>监控所有服务器是否正常运行：通过发送命令返回监控服务器的运行状态，处理监控主服务器、从服务器外，<strong>哨兵之间也相互监控</strong>。</li>
<li>故障切换：当哨兵监测到master宕机，会自动将slave切换成master，然后通过<strong>发布订阅模式</strong>通知其他的从服务器，修改配置文件，让它们切换master。同时那台有问题的旧主也会变为新主的从，也就是说当旧的主即使恢复时，并不会恢复原来的主身份，而是作为新主的一个从。</li>
</ul>
<p><strong>哨兵模式的优缺点</strong></p>
<p>优点：</p>
<ul>
<li>哨兵模式是基于主从模式的，解决了主从模式中master故障后不可以自动切换问题</li>
</ul>
<p>缺点：</p>
<ul>
<li>浪费资源，集群里所有节点保存的都是全量数据，数据量过大时，主从同步会严重影响性能。</li>
<li>Redis主机宕机后，头片选举结束之前，谁也不知道主机和从机是谁，此时Redis也会开启保护机制，禁止写操作，直到选举出了新的Redis主机。</li>
<li>只有一个master库执行写请求，写操作受单机性能瓶颈影响。</li>
</ul>
<h3 id="redis-cluster待补充">Redis Cluster(待补充)</h3>
<p><strong>Redis Cluster</strong>是一种服务端Sharding技术，Redis Cluster并没有使用一致性hash，而是采用<strong>slot(槽)</strong>的概念，一共分成<strong>16384</strong>个槽。将请求发送到任意节点，接收到请求的节点会将查询请求发送到正确的节点上执行。</p>
<h2 id="redis的分布式问题">Redis的分布式问题</h2>
<h3 id="redis实现分布式锁">Redis实现分布式锁</h3>
<h4 id="setnx命令">(1)<code>setnx</code>命令</h4>
<ul>
<li><strong>获取锁</strong>：使用<code>setnx</code>命令，在获取锁时调用<code>setnx</code>，如果返回0，则该锁正在被别人使用；如果返回1，则成功获取锁。为了释放锁，应当用<code>expire</code>命令设置锁过期时间。</li>
<li><strong>释放锁</strong>：如果锁存在，则执行Redis的<code>del</code>命令。</li>
</ul>
<p>如果在<code>setnx</code>执行之后执行<code>expire</code>之前进程意外crash或者要重启维护了，那这个锁就释放不了。这时应该使用<code>set</code>命令，set命令有非常复杂的参数，相当于合成了<code>setnx</code>和<code>expire</code>两条命令。</p>
<h4 id="redisson分布式锁待补充">(2)Redisson分布式锁(待补充)</h4>
<h3 id="缓存穿透缓存击穿缓存雪崩怎么避免">缓存穿透、缓存击穿、缓存雪崩，怎么避免？</h3>
<p><strong>缓存穿透：</strong>由于缓存系统发生故障或者用户频繁查询系统中不存在(数据库和缓存中都不存在)的数据，这时请求穿过缓存不断被发送到数据库，导致数据库过载，进而引发一连串并发问题。</p>
<blockquote>
<p>避免缓存穿透：</p>
<p>1、使用布隆过滤器；</p>
<p>2、缓存空对象：数据库查询结果为空，依然将这个结果缓存，这样下次查的时候直接返回结果(注意设置短一点的过期时间)。</p>
</blockquote>
<p><strong>缓存击穿</strong>：在缓存中缓存一个热点数据，但该数据在某个时间点过期或被删除，此时正好有大量并发请求访问该数据。这些请求无法从缓存中获取到数据，导致大量请求穿透到数据库，造成数据库压力激增，可能引起数据库服务的宕机。</p>
<blockquote>
<p>避免缓存击穿：</p>
<p>1、使用互斥锁：在获取数据前先判断是否存在缓存，不存在则加锁，然后从数据库中读取数据并设置到缓存中，最后释放锁。</p>
<p>2、预加载机制：在数据即将过期时提前获取数据并刷新缓存。</p>
</blockquote>
<p><strong>缓存雪崩</strong>：在同一时刻由于大量缓存失效，导致请求都去查询数据库，可能导致数据库宕机，使整个系统崩溃。</p>
<blockquote>
<p>避免缓存雪崩：</p>
<p>1、请求加锁</p>
<p>2、失效更新：为每一个缓存增加过期标记来记录缓存是否失效，如果缓存标记失效，则更新缓存数据。</p>
<p>3、设置不同的失效时间：为不同的数据设置不同的失效时间，防止在同一时刻有大量缓存失效。</p>
</blockquote>
<h2 id="待补充">...(待补充)</h2>
<h1 id="spring">Spring</h1>
<h2 id="什么是spring">什么是Spring?</h2>
<p>Spring 是一款开源的轻量级Java开发框架，旨在提高开发人员的开发效率以及系统的可维护性。</p>
<h2 id="spring由哪些模块组成">Spring由哪些模块组成？</h2>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804152407247.png" alt=""><figcaption>image-20230804152407247</figcaption>
</figure>
<h3 id="core-container">Core Container</h3>
<p>Spring框架的核心模块，也可以说是基础模块，主要提供IoC依赖注入功能的支持。Spring 其他所有的功能基本都需要依赖于该模块，我们从上面那张Spring各个模块的依赖关系图就可以看出来。</p>
<ul>
<li><strong>spring-core</strong>：Spring框架基本的核心工具类。</li>
<li><strong>spring-beans</strong>：提供对bean的创建、配置和管理等功能的支持。</li>
<li><strong>spring-context</strong>：提供对国际化、事件传播、资源加载等功能的支持。</li>
<li><strong>spring-expression</strong>：提供对表达式语言（Spring Expression Language）SpEL的支持，只依赖于 core模块，不依赖于其他模块，可以单独使用。</li>
</ul>
<h3 id="aop">AOP</h3>
<ul>
<li><strong>spring-aspects</strong>：该模块为与AspectJ的集成提供支持。</li>
<li><strong>spring-aop</strong>：提供了面向切面的编程实现。</li>
<li><strong>spring-instrument</strong>：提供了为JVM添加代理（agent）的功能。具体来讲，它为Tomcat提供了一个织入代理，能够为Tomcat传递类文件，就像这些文件是被类加载器加载的一样。没有理解也没关系，这个模块的使用场景非常有限。</li>
</ul>
<h3 id="data-accessintegration">Data Access/Integration</h3>
<ul>
<li><strong>spring-jdbc</strong>：提供了对数据库访问的抽象JDBC。不同的数据库都有自己独立的API用于操作数据库，而 Java程序只需要和JDBC API交互，这样就屏蔽了数据库的影响。</li>
<li><strong>spring-tx</strong>：提供对事务的支持。</li>
<li><strong>spring-orm</strong>：提供对Hibernate、JPA、iBatis 等ORM框架的支持。</li>
<li><strong>spring-oxm</strong>：提供一个抽象层支撑OXM(Object-to-XML-Mapping)，例如JAXB、Castor、XMLBeans、JiBX 和 XStream 等。</li>
<li><strong>spring-jms</strong> : 消息服务。自 Spring Framework4.1以后，它还提供了对spring-messaging模块的继承</li>
</ul>
<h3 id="spring-web">Spring Web</h3>
<ul>
<li><strong>spring-web</strong>：对Web功能的实现提供一些最基础的支持。</li>
<li><strong>spring-webmvc</strong>：提供对SpringMVC的实现。</li>
<li><strong>spring-websocket</strong>：提供了对WebSocket的支持，WebSocket可以让客户端和服务端进行双向通信。</li>
<li><strong>spring-webflux</strong>：提供对WebFlux的支持。WebFlux 是Spring Framework5.0中引入的新的响应式框架。与Spring MVC不同，它不需要Servlet API，是完全异步。</li>
</ul>
<h3 id="messaging">Messaging</h3>
<p><strong>spring-messaging</strong> 是从Spring4.0开始新加入的一个模块，主要职责是为Spring框架集成一些基础的报文传送应用。</p>
<h3 id="spring-test">Spring Test</h3>
<p>Spring团队提倡测试驱动开发(TDD)。有了控制反转(IoC)的帮助，单元测试和集成测试变得更简单。</p>
<p>Spring的测试模块对JUnit(单元测试框架)、TestNG(类似JUnit)、Mockito(主要用来Mock对象)、PowerMock（解决Mockito的问题比如无法模拟final,static，private方法）等等常用的测试框架支持的都比较好。</p>
<h2 id="spring-ioc控制反转">Spring IoC(控制反转)</h2>
<h3 id="什么是ioc">什么是IoC?</h3>
<p>IoC(Inversion of Controll,控制反转)是一种设计思想，而不是一个具体的技术实现。<strong>IoC的思想就是将原本在程序中手动创建对象的控制权交给Spring框架来管理</strong>。</p>
<p>将对象之间的相互依赖关系交给IoC容器来管理，并由IoC容器完成对象的注入。这样可以很大程度上简化应用的开发，把应用从复杂的依赖关系中解放出来。IoC容器就像是一个工厂一样，当我们需要创建一个对象的时候，只需要配置好配置文件/注解即可，完全不用考虑对象是如何被创建出来的。</p>
<p>优点：<strong>降低代码耦合度</strong>和<strong>集中资源统一管理，简化开发</strong>。</p>
<h3 id="控制反转ioc的作用是什么">控制反转(IoC)的作用是什么？</h3>
<p>降低代码之间的耦合度，管理对象的创建和依赖关系的维护。</p>
<h3 id="spring-ioc的实现机制">Spring IoC的实现机制</h3>
<p>简单来说就是解析xml文件获取到对象信息，通过反射获取字节码文件，然后通过字节码文件创建对象，并且在创建对象的过程中使用了工厂模式。</p>
<h3 id="beanfactory和applicationcontext有什么区别">BeanFactory和ApplicationContext有什么区别？</h3>
<p><strong>(1)作用：</strong></p>
<p>BeanFactory接口是Spring里面最底层的接口，包含了各种Bean的定义，读取bean配置文档，管理bean的加载、实例化，控制bean的生命周期，维护bean之间的依赖关系。</p>
<p>ApplicationContext接口作为BeanFactory的派生，除了提供BeanFactory所具有的功能外，还有额外的功能</p>
<ul>
<li>提供在监听器中注册bean的事件</li>
<li>载入多个（有继承关系）上下文 ，使得每一个上下文都专注于一个特定的层次，比如应用的web层</li>
<li>同时加载多个配置文件</li>
<li>统一的资源文件访问方式</li>
<li>继承MessageSource，因此支持国际化</li>
</ul>
<p><strong>(2)加载方式</strong></p>
<p>BeanFactroy：采用的是<strong>延迟加载</strong>形式来注入Bean，也就是只有在使用到某个bean时，才会对该bean进行加载实例化，这样的弊端很明显，就是如果spring的配置存在问题，那么只有BeanFactory加载后，使用到这个bean时才可以发现问题。</p>
<p>ApplicationContext：ApplicationContext采用的是<strong>预加载机制</strong>，在容器启动时，一次性创建所有的bean。这种可以避免BeanFactory接口中出现的问题，容器启动时就可以发现Spring配置中存在的错误，但缺点是会占用内存空间，并且当配置的bean较多时，程序启动会变慢。</p>
<p><strong>(3)创建方式</strong></p>
<p>BeanFactroy：采用编程的方式创建，如</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">BeanFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XmlBeanFactory</span> (<span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">"beans.xml"</span>)); </span><br></pre></td></tr></table></figure>
<p>ApplicationContext：除了采用编程的方式创建，还可以使用声明的方式创建，在web.xml文件添加如下代码</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230804155120873.png" alt=""><figcaption>image-20230804155120873</figcaption>
</figure>
<p><strong>(4)注册方式</strong></p>
<p>BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用，但两者之间的区别是：BeanFactory需要手动注册，而ApplicationContext则是自动注册。</p>
<h3 id="什么是依赖注入依赖注入的方式有哪些补充">什么是依赖注入，依赖注入的方式有哪些？(补充)</h3>
<p>依赖注入是指程序运行过程中，如果需要调用另一个对象协助时，无需在代码中创建被调用者，而是依赖于外部的注入。</p>
<p>构造器注入</p>
<p>属性注入</p>
<p>接口注入</p>
<h3 id="将一个类声明为bean的注解有哪些">将一个类声明为Bean的注解有哪些？</h3>
<ul>
<li><code>@Component</code>:可标注任意类为<code>Spring</code>组件，如果一个Bean不知道属于哪个层，可使用该注解。</li>
<li><code>@Repository</code>:对应持久层即Dao层，主要用于数据库相关操作。</li>
<li><code>@Service</code>:对应服务层，主要涉及一些复杂的逻辑，需要用到Dao层。</li>
<li><code>@Controller</code>:对应SpringMVC控制层，主要用于接收用户请求并调用<code>Service</code>层返回数据给前端页面。</li>
</ul>
<h3 id="component和bean的区别是什么"><span class="citation" data-cites="Component和">@Component和</span><span class="citation" data-cites="Bean的区别是什么">@Bean的区别是什么</span>？</h3>
<ul>
<li><code>@Component</code>注解作用于类，而<code>@Bean</code>注解作用于方法。</li>
<li><code>@Component</code>通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用<code>@ComponentScan</code>注解定义要扫描的路径从中找出标识了需要装配的类自动装配到Spring的Bean容器中)。<code>@Bean</code>注解通常是我们在标有该注解的方法中定义产生这个bean,<code>@Bean</code>告诉了Spring这是某个类的实例，当我需要它的时候还给我。</li>
<li><code>@Bean</code>注解比<code>@Component</code>注解的自定义更强，而且很多地方我们只能通过<code>@Bean</code>注解来注册bean。比如当我们引用第三方库中的类需要装配到<code>Spring</code>容器时，则只能通过<code>@Bean</code>来实现。</li>
</ul>
<p><code>@Bean</code>注解使用示例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AppConfig</span> {</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> TransferService <span class="title function_">transferService</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TransferServiceImpl</span>();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面的代码相当于下面的xml配置</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;beans&gt;</span><br><span class="line">    &lt;bean id=<span class="string">"transferService"</span> class=<span class="string">"com.acme.TransferServiceImpl"</span>/&gt;</span><br><span class="line">&lt;/beans&gt;</span><br></pre></td></tr></table></figure>
<h3 id="注入bean的注解有哪些">注入Bean的注解有哪些？</h3>
<p>Spring内置的<code>@Autowired</code>以及JDK内置的<code>@Resource</code>和<code>@Inject</code>都可以用于注入Bean。</p>
<table>
<thead>
<tr class="header">
<th>Annotaion</th>
<th>Package</th>
<th>Source</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>@Autowired</code></td>
<td><code>org.springframework.bean.factory</code></td>
<td>Spring 2.5+</td>
</tr>
<tr class="even">
<td><code>@Resource</code></td>
<td><code>javax.annotation</code></td>
<td>Java JSR-250</td>
</tr>
<tr class="odd">
<td><code>@Inject</code></td>
<td><code>javax.inject</code></td>
<td>Java JSR-330</td>
</tr>
</tbody>
</table>
<p><code>@Autowired</code>和<code>@Resource</code>使用的比较多一些。</p>
<h3 id="autowired和resource的区别是什么"><span class="citation" data-cites="Autowired和">@Autowired和</span><span class="citation" data-cites="Resource的区别是什么">@Resource的区别是什么</span>？</h3>
<p><code>@Autowired</code>是Spring内置的注解，默认的注入方式为<code>byType</code>(根据类型进行匹配)，会优先根据接口类型去匹配并注入Bean(接口的实现类)。</p>
<p><strong>问题：</strong>当一个接口存在多个实现类的话，<code>byType</code>这种方式就无法正确注入对象了，因为这个时候Spring会同时找到多个满足条件的选择，默认情况下它自己不知道选择哪一个。</p>
<p>这种情况下，注入方式会变为<code>byName</code>(根据名称进行匹配)，这个名称通常就是类名(首字母小写)。就比如下面代码中的<code>smsService</code>就是这里所说的名称。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> SmsService smsService;</span><br></pre></td></tr></table></figure>
<p>比如，<code>SmsService</code>接口有两个实现类:<code>SmsServiceImpl1</code>和<code>SmsServiceImpl2</code>，且它们都被Spring容器所管理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错，byName 和 byType 都无法匹配到 bean</span></span><br><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> SmsService smsService;</span><br><span class="line"><span class="comment">// 正确注入 SmsServiceImpl1 对象对应的 bean</span></span><br><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="keyword">private</span> SmsService smsServiceImpl1;</span><br><span class="line"><span class="comment">// 正确注入  SmsServiceImpl1 对象对应的 bean</span></span><br><span class="line"><span class="comment">// smsServiceImpl1 就是我们上面所说的名称</span></span><br><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="meta">@Qualifier(value = "smsServiceImpl1")</span></span><br><span class="line"><span class="keyword">private</span> SmsService smsService;</span><br></pre></td></tr></table></figure>
<p>建议通过<code>@Qualifier</code>注解来显式指定名称而不是依赖变量的名称。</p>
<p><code>@Resource</code>属于JDK提供的注解，默认注入方式为<code>byName</code>。如果无法通过名称匹配到对应的Bean,注入方式会变为<code>byType</code>。</p>
<p><code>@Resource</code>有两个比较重要的属性：<code>name</code>(名称)、<code>type</code>(类型)。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Resource {</span><br><span class="line">    String <span class="title function_">name</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">""</span>;</span><br><span class="line">    Class&lt;?&gt; type() <span class="keyword">default</span> Object.class;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错，byName 和 byType 都无法匹配到 bean</span></span><br><span class="line"><span class="meta">@Resource</span></span><br><span class="line"><span class="keyword">private</span> SmsService smsService;</span><br><span class="line"><span class="comment">// 正确注入 SmsServiceImpl1 对象对应的 bean</span></span><br><span class="line"><span class="meta">@Resource</span></span><br><span class="line"><span class="keyword">private</span> SmsService smsServiceImpl1;</span><br><span class="line"><span class="comment">// 正确注入 SmsServiceImpl1 对象对应的 bean（比较推荐这种方式）</span></span><br><span class="line"><span class="meta">@Resource(name = "smsServiceImpl1")</span></span><br><span class="line"><span class="keyword">private</span> SmsService smsService;</span><br></pre></td></tr></table></figure>
<p>总结：</p>
<ul>
<li><code>@Autowired</code>是Spring提供的注解，<code>@Resource</code>是JDK提供的注解。</li>
<li><code>@Autowired</code>默认的注入方式为<code>byType</code>(根据类型匹配)，<code>@Resource</code>可以通过<code>name</code>属性来显示指定名称。</li>
</ul>
<h3 id="bean的作用域有哪些">Bean的作用域有哪些？</h3>
<p>常见的作用域有<code>Singleton</code>(单例)、<code>Prototype</code>(原型)、<code>Request</code>(请求级别)、<code>Session</code>(会话级别)和<code>Global Session</code>(全局会话)。</p>
<ul>
<li><code>Singleton</code>:Singleton是单例模式，当实例类型为单例模式时，在Spring IoC容器中只会存在一个共享的Bean实例，无论有多少个Bean引用它，都始终指向同一个Bean对象。该模式在多线程下是不安全的。Singleton是Spring中的默认作用域。</li>
<li><code>Prototype</code>:<code>Prototype</code>是原型模式，每次通过Spring容器获取<code>Prototype</code>定义的Bean时，容器都将创建一个新的Bean实例，每个Bean实例都有自己的属性和状态，而Singleton全局只有一个对象。</li>
<li><code>Request</code>:仅Web应用可用，每次HTTP请求都会产生一个新的Bean(请求Bean),该Bean仅在当前Http请求内有效。</li>
<li><code>Session</code>:每次来自新Session的HTTP请求都会产生一个新的Bean(会话Bean),该Bean仅在当前Http Session内有效。</li>
<li><code>Global Session</code>:在一个全局的HTTP Session中容器会返回该Bean的同一个实例，且仅在使用<code>Protlet Context</code>时有效。</li>
</ul>
<h3 id="bean是线程安全的吗">Bean是线程安全的吗？</h3>
<p>Bean是否线程安全，取决于其作用域和状态。</p>
<p>Prototype作用域下，每次获取都会创建一个新的Bean实例，不存在资源竞争问题，所以不存在线程安全问题。</p>
<p>Singleton作用域下，IoC容器中只有唯一的Bean实例，可能会存在资源竞争问题(取决于Bean是否有状态)。如果这个Bean是有状态的话，那就存在线程安全问题(有状态Bean是指包含可变的成员变量的对象)。</p>
<p>不过，大部分Bean实际都是无状态(没有定义可变的成员变量)的(比如Dao、Service)，这种情况下Bean是线程安全的。</p>
<p>对于有状态单例Bean的线程安全问题，常见的有两种解决方案：</p>
<ul>
<li>在Bean中尽量避免定义可变的成员变量。</li>
<li>在类中定一一个<code>ThreadLocal</code>成员变量，将需要的可变成员变量保存在<code>ThreadLocal</code>中(推荐)。</li>
</ul>
<h3 id="bean的生命周期">Bean的生命周期</h3>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817165247620.png" alt="image-20230817165247620" style="zoom:80%;"></p>
<p>具体过程如下：</p>
<p>(1)实例化一个Bean</p>
<p>(2)按照Spring上下文对实例化的Bean进行配置</p>
<p>(3)如果Bean实现了<code>BeanNameAware</code>接口，则会执行它实现的<code>setBeanName(String)</code>方法，该方法传递的参数是Spring配置文件中Bean的id值。</p>
<p>(4)如果Bean实现了<code>BeanFactoryAware</code>接口，则会执行它实现的<code>setBeanFactory(BeanFactory)</code>方法，该方法传递的参数是Spring工厂自身。</p>
<p>(5)如果Bean实现了<code>ApplicationContextAware</code>接口，则会执行<code>setApplicationContext(ApplicationContext)</code>方法，该方法传入的参数是Spring上下文。</p>
<p>(6)如果Bean关联了<code>BeanPostProcessor</code>接口，则会执行<code>postProcessBeforeInitialization(Object obj,String s)</code>方法，该方法在Bean初始化前调用，常用于定义初始化Bean的前置工作，比如系统缓存的初始化。</p>
<p>(7)如果Bean在Spring配置文件中配置了<code>init-method</code>属性，则会自动执行其配置的初始化方法。</p>
<p>(8)如果bean关联了<code>BeanPostProcessor</code>接口，将会执行<code>postProcessAfterInitialization(Object obj,String s)</code>方法。至此，Bean的初始化工作就完成了，应用程序可以开始使用Bean实例了。</p>
<p>(9)当销毁Bean的时候，如果Bean实现了<code>DisposableBean</code>接口，则Spring会在退出前调用实现类的<code>destroy</code>方法。</p>
<p>(10)如果在某个Bean的Spring配置文件中配置了<code>destroy-method</code>属性，则在Bean被销毁前会自动调用其配置的销毁方法。</p>
<h2 id="spring-aop面向切面编程">Spring AOP(面向切面编程)</h2>
<h3 id="什么是spring-aop">什么是Spring AOP?</h3>
<p><code>AOP</code>(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关，却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来，便于减少系统的重复代码，降低模块间的耦合度，并有利于未来的可拓展性和可维护性。</p>
<p>Spring AOP就是基于动态代理的，如果要代理的对象，实现了某个接口，那么Spring AOP会使用<strong>JDK Proxy</strong>去创建代理对象，而对于没有实现接口的对象，就无法使用 JDK Proxy 去进行代理了，这时候 Spring AOP 会使用 <strong>Cglib</strong> 生成一个被代理对象的子类来作为代理，如下图所示：</p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/230ae587a322d6e4d09510161987d346.jpeg"></p>
<p>当然你也可以使用 <strong>AspectJ</strong> ！Spring AOP已经集成了AspectJ ，AspectJ应该算的上是Java生态系统中最完整的AOP框架了。</p>
<h3 id="aop的一些名词">AOP的一些名词</h3>
<table>
<thead>
<tr class="header">
<th>术语</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>目标(Target)</td>
<td>被通知的对象</td>
</tr>
<tr class="even">
<td>代理(Proxy)</td>
<td>向目标对象应用通知之后创建的代理对象</td>
</tr>
<tr class="odd">
<td>连接点(JoinPoint)</td>
<td>目标对象的所属类中，定义的所有方法均为连接点</td>
</tr>
<tr class="even">
<td>切点(Pointcut)</td>
<td>被切面拦截/增强的连接点(切入点一定是连接点，连接点不一定是且度电)</td>
</tr>
<tr class="odd">
<td>通知(Advice)</td>
<td>增强的逻辑/代码，也即拦截到目标对象的连接点之后要做的事情</td>
</tr>
<tr class="even">
<td>切面(Aspect)</td>
<td>切入点(Pointcut)+通知(Advice)</td>
</tr>
<tr class="odd">
<td>织入(Weaving)</td>
<td>将通知应用到目标对象，进而生成代理对象的过程动作</td>
</tr>
</tbody>
</table>
<h3 id="aspectj定义的通知类型有哪些">AspectJ定义的通知类型有哪些？</h3>
<ul>
<li><p><code>Before</code>(前置通知)：目标对象的方法调用之前触发</p></li>
<li><p><code>After</code>(后置通知)：目标对象的方法调用之后触发</p></li>
<li><p><code>AfterReturning</code>(返回通知)：目标对象的方法调用完成，在返回结果值之后触发</p></li>
<li><p><code>AfterThrowing</code>(异常通知)：目标对象的方法运行中抛出/触发异常后触发。<code>AfterReturning</code>和 <code>AfterThrowing</code>两者互斥。如果方法调用成功无异常，则会有返回值；如果方法抛出了异常，则不会有返回值。</p></li>
<li><p><code>Around</code>(环绕通知)：编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种，因为它可以直接拿到目标对象，以及要执行的方法，所以环绕通知可以任意的在目标对象的方法调用前后搞事，甚至不调用目标对象的方法。</p></li>
</ul>
<h3 id="多个切面的执行顺序如何控制">多个切面的执行顺序如何控制？</h3>
<p>1、通常使用<code>@Order</code>注解直接定义切面顺序</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 值越小优先级越高</span></span><br><span class="line"><span class="meta">@Order(3)</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoggingAspect</span> <span class="keyword">implements</span> <span class="title class_">Ordered</span> {</span><br></pre></td></tr></table></figure>
<p>2、实现<code>Ordered</code>接口重写<code>getOrder</code>方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LoggingAspect</span> <span class="keyword">implements</span> <span class="title class_">Ordered</span> {</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ....</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getOrder</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 返回值越小优先级越高</span></span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="spring-aop的两种代理方式">Spring AOP的两种代理方式</h3>
<h4 id="jdk动态代理">JDK动态代理</h4>
<p><strong>从JVM角度来说，动态代理是在运行时动态生成类字节码，并加载到JVM中的。</strong></p>
<p>说到动态代理，Spring AOP、RPC框架应该是两个不得不提的，它们的实现都依赖了动态代理。</p>
<p><strong>动态代理在我们日常开发中使用的相对较少，但是在框架中的几乎是必用的一门技术。学会了动态代理之后，对于我们理解和学习各种框架的原理也非常有帮助。</strong></p>
<p>就Java来说，动态代理的实现方式有很多种，比如 <strong>JDK动态代理</strong>、<strong>CGLIB动态代理</strong>等等。</p>
<p><strong>在Java动态代理机制中<code>InvocationHandler</code>接口和<code>Proxy</code>类是核心。</strong></p>
<p><code>Proxy</code>类中使用频率最高的方法是：<code>newProxyInstance()</code>，这个方法主要用来生成一个代理对象。</p>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230808164719217.png" alt=""><figcaption>image-20230808164719217</figcaption>
</figure>
<ul>
<li>loader:类加载器，用于加载代理对象</li>
<li>interfaces:被代理类实现的一些接口</li>
<li>h:实现了InvocationHandler接口的对象</li>
</ul>
<p>要实现动态代理的话，还必须需要实现<code>InvocationHandler</code>来自定义处理逻辑。当我们的动态代理对象调用一个方法时，这个方法的调用就会被转发到实现<code>InvocationHandler</code>接口类的<code>invoke</code>方法来调用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">InvocationHandler</span> {</span><br><span class="line">     <span class="comment">//当你使用代理对象调用方法的时候实际会调用到这个方法</span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span></span><br><span class="line">        <span class="keyword">throws</span> Throwable;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li>proxy:动态生成的代理类</li>
<li>method:与代理类对象调用的方法相对应</li>
<li>args:当前method方法的参数</li>
</ul>
<p>也就是说：<strong>你通过<code>Proxy</code>类的 <code>newProxyInstance()</code>创建的代理对象在调用方法的时候，实际会调用到实现<code>InvocationHandler</code>接口的类的<code>invoke()</code>方法。</strong>你可以在<code>invoke()</code>方法中自定义处理逻辑，比如在方法执行前后做什么事情。</p>
<p>JDK动态代理使用步骤：</p>
<p>(1)定义一个接口及其实现类；</p>
<p>(2)自定义<code>InvocationHandler</code>并重写<code>invoke</code>方法，在<code>invoke</code>方法中我们会调用原生方法（被代理类的方法）并自定义一些处理逻辑；</p>
<p>(3)通过<code>Proxy.newProxyInstance(ClassLoader loader,Class&lt;?&gt;[] interfaces,InvocationHandler h)</code>方法创建代理对象；</p>
<blockquote>
<p>IDEA上例子</p>
</blockquote>
<h4 id="cglib动态代理待补充">CGLib动态代理(待补充)</h4>
<p>CGLib(Code Generation Library)，是一个高性能的代码生成类库，可以在运行期间扩展Java类和实现Java接口。CGLib包的底层通过字节码处理框架ASM来实现，通过转换字节码生成新的类。</p>
<h4 id="jdk动态代理和cglib动态代理的区别">JDK动态代理和CGLib动态代理的区别</h4>
<p>JDK只能为接口创建代理实例，而对于没有通过接口定义业务方法的类，则只能通过CGLib创建动态代理来实现。</p>
<h2 id="spring事务待补充">Spring事务(待补充)</h2>
<h2 id="spring中用到了哪些设计模式">Spring中用到了哪些设计模式？</h2>
<ul>
<li>工厂模式：Spring使用工厂模式通过<code>BeanFactory</code>、<code>ApplicationContext</code>创建Bean对象。</li>
<li>代理模式：Spring AOP功能的实现。</li>
<li>单例模式：Spring中的Bean默认都是单例的。</li>
<li>包装器模式：我们的项目需要连接多个数据库，而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。</li>
<li>观察者模式：Spring事件驱动模型就是观察者模式很经典的一个应用。</li>
<li>适配器模式：Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也使用到了适配器模式适配<code>Controller</code>。</li>
</ul>
<h1 id="springmvc">SpringMVC</h1>
<h2 id="对springmvc的理解">对SpringMVC的理解</h2>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20210809181452421.png"></p>
<p><code>MVC</code>是模型(Model)、视图(View)和控制器(Controller)的简写，其核心思想是通过将业务逻辑、数据、显示分离来组织代码。</p>
<ul>
<li>模型(Model):用于封装业务逻辑处理。</li>
<li>视图(View):用于数据展现和操作界面。</li>
<li>控制器(Controller):负责转发请求，对请求进行处理。</li>
</ul>
<p><code>MVC</code>是一种设计模式，<code>Spring MVC</code>是一款很优秀的MVC框架。Spring MVC可以帮助我们进行更简洁的Web层的开发，并且它天生与Spring框架集成。Spring MVC下我们一般把后端项目分为<code>Service</code>层(处理层)、<code>Dao</code>层(数据库操作)、<code>Entity</code>层(实体类)、<code>Controller</code>层(控制层，返回数据给前台页面)。</p>
<h2 id="springmvc的核心组件有哪些">SpringMVC的核心组件有哪些？</h2>
<ul>
<li><code>DispatcherServlet</code>:<strong>前端控制器</strong>，负责接收请求、分发，并给予客户端响应。</li>
<li><code>HandlerMapping</code>:<strong>处理器映射器</strong>，根据URI去匹配查找能处理的<code>Handler</code>，并会将请求涉及到的拦截器和<code>Handler</code>一起封装。</li>
<li><code>HandlerAdapter</code>:<strong>处理器适配器</strong>，根据<code>HandlerMapping</code>找到的<code>Handler</code>，适配执行对应的 <code>Handler</code>（调用具体的方法对用户发来的请求进行处理)。</li>
<li><code>Handler</code>:<strong>请求处理器</strong>，处理实际请求的处理器。</li>
<li><code>ViewResolver</code>:<strong>视图解析器</strong>，根据<code>Handler</code>返回的逻辑视图/视图，解析并渲染真正的视图，并传递给<code>DispatcherServlet</code>响应客户端。</li>
</ul>
<h2 id="springmvc工作原理">SpringMVC工作原理</h2>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230817212919655.png" alt=""><figcaption>SpringMVC工作流程</figcaption>
</figure>
<p>具体流程如下：</p>
<p>(1)客户端发起HTTP请求；客户端将请求提交到<code>DispatcherServlet</code>。</p>
<p>(2)寻找处理器：由<code>DispatcherServlet</code>控制器查询一个或多个<code>HandlerMapping</code>,找到处理该请求的<code>Controller</code>。</p>
<p>(3)调用处理器：<code>DispatcherServlet</code>将请求提交到<code>Controller</code></p>
<p>(4)调用业务处理逻辑并返回结果：<code>Controller</code>在掉哦用业务处理逻辑后，返回<code>ModelAndView</code>。</p>
<p>(5)处理视图映射并返回模型：<code>DispatcherServlet</code>查询一个或多个<code>ViewResoler</code>视图解析器，找到ModelAndView指定的视图。</p>
<p>(6)HTTP响应：视图负责将结果在客户端浏览器上渲染和展示。</p>
<h2 id="统一异常处理怎么做">统一异常处理怎么做？</h2>
<p>使用注解的方式实现统一异常处理，用到了<code>@ControllerAdvice</code>和<code>@ExceptionHandler</code>两个注解。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@ControllerAdvice</span></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GlobalExceptionHandler</span> {</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ExceptionHandler(BaseException.class)</span></span><br><span class="line">    <span class="keyword">public</span> ResponseEntity&lt;?&gt; handleAppException(BaseException ex, HttpServletRequest request) {</span><br><span class="line">      <span class="comment">//......</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ExceptionHandler(value = ResourceNotFoundException.class)</span></span><br><span class="line">    <span class="keyword">public</span> ResponseEntity&lt;ErrorReponse&gt; <span class="title function_">handleResourceNotFoundException</span><span class="params">(ResourceNotFoundException ex, HttpServletRequest request)</span> {</span><br><span class="line">      <span class="comment">//......</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这种异常处理方式下，会给所有或者指定的<code>Controller</code>织入异常处理逻辑(AOP)，当Controller中的方法抛出异常的时候，由被<code>@ExceptionHandler</code>注解修饰的方法处理。</p>
<p><code>ExceptionHandlerMethodResolver</code>中<code>getMappedMethod</code>方法决定了异常具体被哪个<code>@ExceptionHandler</code>注解修饰的方法处理。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line">	<span class="keyword">private</span> Method <span class="title function_">getMappedMethod</span><span class="params">(Class&lt;? extends Throwable&gt; exceptionType)</span> {</span><br><span class="line">		List&lt;Class&lt;? <span class="keyword">extends</span> <span class="title class_">Throwable</span>&gt;&gt; matches = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//找到可以处理的所有异常信息。mappedMethods 中存放了异常和处理异常的方法的对应关系</span></span><br><span class="line">		<span class="keyword">for</span> (Class&lt;? <span class="keyword">extends</span> <span class="title class_">Throwable</span>&gt; mappedException : <span class="built_in">this</span>.mappedMethods.keySet()) {</span><br><span class="line">			<span class="keyword">if</span> (mappedException.isAssignableFrom(exceptionType)) {</span><br><span class="line">				matches.add(mappedException);</span><br><span class="line">			}</span><br><span class="line">		}</span><br><span class="line">    <span class="comment">// 不为空说明有方法处理异常</span></span><br><span class="line">		<span class="keyword">if</span> (!matches.isEmpty()) {</span><br><span class="line">      <span class="comment">// 按照匹配程度从小到大排序</span></span><br><span class="line">			matches.sort(<span class="keyword">new</span> <span class="title class_">ExceptionDepthComparator</span>(exceptionType));</span><br><span class="line">      <span class="comment">// 返回处理异常的方法</span></span><br><span class="line">			<span class="keyword">return</span> <span class="built_in">this</span>.mappedMethods.get(matches.get(<span class="number">0</span>));</span><br><span class="line">		}</span><br><span class="line">		<span class="keyword">else</span> {</span><br><span class="line">			<span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">		}</span><br><span class="line">	}</span><br></pre></td></tr></table></figure>
<p>从源代码看出：<strong><code>getMappedMethod()</code>会首先找到可以匹配处理异常的所有方法信息，然后对其进行从小到大的排序，最后取最小的那一个匹配的方法(即匹配度最高的那个)</strong></p>
<h1 id="mybatis">Mybatis</h1>
<h2 id="和的区别是什么"><code>#{}</code>和<code>${}</code>的区别是什么？</h2>
<p><code>${}</code>是Properties文件中的变量占位符，它可以用于标签属性值和sql内部，属于静态文本替换，比如<code>${driver}</code>会被静态替换为<code>com.mysql.jdbc.Driver</code>。</p>
<p><code>#{}</code>是sql的参数占位符，Mybatis会将sql中的<code>#{}</code>替换为<code>?</code>号，在sql执行前会使用<code>PreparedStatement</code>的参数设置方法，按序给sql的<code>?</code>号占位符设置参数值，比如<code>ps.setInt(0,parameterValue)</code>,<code>#{item.name}</code>的取值方式为使用反射从参数对象中获取item对象的name属性值，相当于<code>param.getItem().getName()</code>。</p>
<h2 id="dao接口的工作原理是什么待补充">Dao接口的工作原理是什么？(待补充)</h2>
<h2 id="mybatis是如何进行分页的分页插件的原理是什么">Mybatis是如何进行分页的？分页插件的原理是什么？</h2>
<p>(1)Mybatis使用RowBounds对象进行分页，它是针对ResultSet结果集执行的内存分页，而非物理分页。(2)可以在SQL内直接数写带有物理分页的参数来完成物理分页功能。(3)也可以使用分页插件来完成物理分页。</p>
<p><strong>分页插件的基本原理</strong>是使用Mybatis提供的插件接口，实现自定义插件，在插件的拦截方法内拦截待执行的sql,然后重写sql，根据dialect方言，添加对应的物理分页语句和物理分页参数。</p>
<h2 id="简述mybatis的插件运行原理以及如何编写一个插件">简述MyBatis的插件运行原理，以及如何编写一个插件</h2>
<p>Mybatis仅可以编写针对<code>ParameterHandler</code>、<code>ResultSetHandler</code>、<code>StatementHandler</code>、<code>Executor</code>这4种接口的插件，Mybatis使用JDK的动态代理，为需要拦截的接口生成代理对象以实现接口方法拦截功能，每当执行这4种接口对象的方法时，就会进入拦截方法，具体就是<code>InnocationHandler</code>的<code>invoke()</code>方法，当然，只会拦截那些你指定需要拦截的方法。</p>
<p>实现Mybatis的<code>Interceptor</code>接口并复写<code>intercept()</code>方法，然后再给插件编写注解，指定要拦截哪一个接口的哪些方法即可，还要记得在配置文件中配置你编写的插件。</p>
<h2 id="mybatis动态sql是做什么的都有哪些动态sql">Mybatis动态sql是做什么的？都有哪些动态sql？</h2>
<p>Mybatis动态sql可以让我们在xml映射文件内，以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。其执行原理为，使用OGNL从sql参数对象中计算表达式的值，根据表达式的值动态拼接sql,以此来完成动态sql的功能。</p>
<p>Mybatis提供了9种动态sql标签</p>
<ul>
<li><code>&lt;if&gt;&lt;/if&gt;</code></li>
<li><code>&lt;where&gt;&lt;/where&gt;(trim,set)</code></li>
<li><code>&lt;choose&gt;&lt;/choose&gt;(when,otherwise)</code></li>
<li><code>&lt;foreach&gt;&lt;/foreach&gt;</code></li>
<li><code>&lt;bind/&gt;</code></li>
</ul>
<h2 id="mybatis是否支持延迟加载它的实现原理是什么">Mybatis是否支持延迟加载？它的实现原理是什么？</h2>
<p>Mybatis仅支持association关联对象和collection关联集合对象的延迟加载，association指的就是一对一，collection指的就是一对多查询。在Mybatis配置文件中，可以配置是否启用延迟加载<code>lazyLoadingEnabled=true|false</code>。</p>
<p>它的原理是，使用<code>CGLib</code>创建目标对象的代理对象，当调用目标方法时，进入拦截器方法，比如调用<code>a.getB().getName()</code>,拦截器<code>invoke()</code>方法发先<code>a.getB()</code>是null值，那么就会单独发送是先保存好的查询关联B对象的sql，把B查询上来，然后调用<code>a.setB(b)</code>，于是a的对象b属性就有值了，接着完成<code>a.getB().getName()</code>方法的调用。这就是延迟加载的基本原理。</p>
<h2 id="mybatis都有哪些executor执行器">Mybatis都有哪些Executor执行器？</h2>
<p><code>SimpleExecutor</code>:每执行一次update或select，就开启一个Statement对象，用完立刻关闭Statement对象。</p>
<p><code>ReuseExecutor</code>:执行update或select，以sql作为key查找Statement对象，存在就使用，不存在就创建，用完后，不关闭Statement对象，而是放置于<code>Map&lt;String,Statement&gt;</code>内，供下一次使用。简言之，就是重复使用Statement对象。</p>
<p><code>BatchExecutor</code>:执行update(没有select,JDBC批处理不支持select)，将所有sql都添加到批处理中(addBatch(),等待统一执行(executeBatch())),它缓存了多个Statement对象，每个Statement对象都是addBatch()完毕后，等待逐一执行executeBatch()批处理，与JDBC批处理相同。</p>
<blockquote>
<p>作用范围：<code>Executor</code>的这些特点，都严格限制在SqlSession声明周期范围内。</p>
</blockquote>
<h2 id="为什么说mybatis是半自动orm映射工具">为什么说Mybatis是半自动ORM映射工具？</h2>
<p>Hibernate是全自动ORM映射工具，使用Hibernate查询关联对象或者关联集合对象时，可以根据对象关系模型直接获取，所以它是全自动的。</p>
<p>Mybatis在查询关联对象或关联集合对象时，需要手动编写sql来完成，所以Mybatis是半自动ORM映射工具。</p>
<h2 id="mybatisplus待补充">MybatisPlus(待补充)</h2>
<h1 id="springboot">SpringBoot</h1>
<h1 id="springcloud">SpringCloud</h1>
<h1 id="springsecurity">SpringSecurity</h1>
<h1 id="分布式相关">分布式相关</h1>
<h1 id="rabbitmq">RabbitMQ</h1>
<h2 id="为什么要引入消息队列呢举几个应用场景待补充">为什么要引入消息队列呢？举几个应用场景(待补充)</h2>
<blockquote>
<p>需要给出具体的场景</p>
</blockquote>
<ul>
<li>异步处理</li>
<li>应用解耦</li>
<li>流量削峰</li>
</ul>
<h1 id="docker">Docker</h1>
<h1 id="elastissearch">ElastisSearch</h1>
<h1 id="设计模式">设计模式</h1>
<h2 id="spring中用到了哪些设计模式-1">Spring中用到了哪些设计模式？</h2>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230628161332880.png" alt=""><figcaption>image-20230628161332880</figcaption>
</figure>
<h2 id="单例模式">单例模式</h2>
<blockquote>
<p>单例模式的常见模式有<strong>懒汉模式(线程安全)、饿汉模式、静态内部类、双重校验锁</strong>。</p>
</blockquote>
<ol type="1">
<li>懒汉式(线程安全)：</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//懒汉模式(线程安全)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LazySingleton</span> {</span><br><span class="line">    <span class="keyword">private</span>  <span class="keyword">static</span> LazySingleton instance;</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">LazySingleton</span><span class="params">()</span>{}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> LazySingleton <span class="title function_">getInstance</span><span class="params">()</span>{</span><br><span class="line">        <span class="keyword">if</span>(instance==<span class="literal">null</span>){</span><br><span class="line">            instance=<span class="keyword">new</span> <span class="title class_">LazySingleton</span>();</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>(2)饿汉式</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//饿汉式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HungrySingleton</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> HungrySingleton instance=<span class="keyword">new</span> <span class="title class_">HungrySingleton</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">HungrySingleton</span><span class="params">()</span>{}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> HungrySingleton <span class="title function_">getInstance</span><span class="params">()</span>{</span><br><span class="line">        <span class="keyword">return</span> instance;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>(3)静态内部类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//静态内部类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SingletonHolder</span>{</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Singleton INSTANCE=<span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">Singleton</span><span class="params">()</span>{}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> Singleton <span class="title function_">getInstance</span><span class="params">()</span>{</span><br><span class="line">        <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>（4）双重校验锁</p>
<blockquote>
<p>双锁模式在懒汉模式的基础上作了进一步优化，<strong>给静态对象的定义加上<code>volatile</code>来保障初始化时对象的唯一性，在获取对象时通过<code>synchronized(Singleton.class)</code>给单例类加锁来保障操作的唯一性</strong>。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//双重校验锁</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Lock2Singleton</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">static</span> Lock2Singleton singleton;   <span class="comment">//1:对象锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">Lock2Singleton</span><span class="params">()</span>{}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Lock2Singleton <span class="title function_">getSingleton</span><span class="params">()</span>{</span><br><span class="line">        <span class="keyword">if</span>(singleton==<span class="literal">null</span>){</span><br><span class="line">            <span class="keyword">synchronized</span> (Lock2Singleton.class){     <span class="comment">//2:synchronized方法锁</span></span><br><span class="line">                <span class="keyword">if</span>(singleton==<span class="literal">null</span>){</span><br><span class="line">                    singleton=<span class="keyword">new</span> <span class="title class_">Lock2Singleton</span>();</span><br><span class="line">                }</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>第一次<code>if(singleton==null)</code>：为了提高代码的执行效率，由于单例模式只要一次创建实例即可，所以当创建了一个实例之后，再次调用<code>getInstance</code>方法就不用进入同步代码块，不用竞争锁。</p>
<p>第二次<code>if(singleton==null)</code>：这个校验时防止二次创建实例。</p>
</blockquote>
<h2 id="工厂模式">工厂模式</h2>
<h1 id="计算机网络">计算机网络</h1>
<h2 id="osi七层模型">OSI七层模型</h2>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230805172947843.png" alt="image-20230805172947843" style="zoom: 80%;"></p>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/20230806104624.png"></p>
<h2 id="tcpip四层模型">TCP/IP四层模型</h2>
<p><img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230815230828549.png" alt="TCP/IP分层模型" style="zoom: 80%;"></p>
<p>TCP/IP四层模型是目前被广泛使用的一种模型，我们可以将TCP/IP模型看作是OSI七层模型的精简版本，由以下四层组成：</p>
<ul>
<li>应用层</li>
<li>传输层</li>
<li>网络层</li>
<li>网络接口层</li>
</ul>
<h2 id="tcp和udp的区别应用场景">TCP和UDP的区别?应用场景？</h2>
<p>(1)<strong>是否面向连接</strong>：<code>UDP</code>在传送数据之前不需要建立连接，而<code>TCP</code>提供面向连接的服务，在传送数据之前需要先建立连接，数据传送后要释放连接。</p>
<p>(2)<strong>是否是可靠传输</strong>：远程主机在收到<code>UDP</code>报文后，不需要给出任何确认。TCP提供可靠的传输服务，TCP在传送数据之前，会有三次握手来建立连接，而且在数据传送时由确认、窗口、重传、拥塞控制等级制。通过TCP连接传输的数据，无差错、不丢失、不重复、并且按序到达。</p>
<p>(3)<strong>是否有状态</strong>：TCP传输是有状态的，这个状态说的是<code>TCP</code>会去记录自己发送消息的状态比如消息是否发送了、是否被接受了等等。而<code>UDP</code>是无状态服务，简单来说就是不管发出去之后的事情了。</p>
<p>(4)<strong>传输效率</strong>：由于使用TCP进行传输的时候多了连接、确认、重传等机制，所以TCP的传输效率要比UDP低很多。</p>
<p>(5)<strong>传输形式</strong>：TCP是面向字节流的，UDP是面向报文的。</p>
<p>(6)<strong>是否提供广播或多播服务</strong>：TCP只支持点对点通信，UDP支持一对一、一对多、多对一、多对多。</p>
<table>
<thead>
<tr class="header">
<th></th>
<th>TCP</th>
<th>UDP</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>是否面向连接</td>
<td>是</td>
<td>否</td>
</tr>
<tr class="even">
<td>是否可靠</td>
<td>是</td>
<td>否</td>
</tr>
<tr class="odd">
<td>是否有状态</td>
<td>是</td>
<td>否</td>
</tr>
<tr class="even">
<td>传输效率</td>
<td>较慢</td>
<td>较快</td>
</tr>
<tr class="odd">
<td>传输形式</td>
<td>字节流</td>
<td>数据报文段</td>
</tr>
<tr class="even">
<td>首部开销</td>
<td>20 ～ 60 bytes</td>
<td>8 bytes</td>
</tr>
<tr class="odd">
<td>是否提供广播或多播服务</td>
<td>否</td>
<td>是</td>
</tr>
</tbody>
</table>
<ul>
<li>UDP一般用于及时通信，比如：语音、视频、直播等等。这些场景对传输数据的准确性要求不是特别高。</li>
<li><strong>TCP用于对传输准确性要求特别高的场景</strong>，比如文件传输、发送和接收邮件、远程登录等等。</li>
</ul>
<h2 id="tcp三次握手">TCP三次握手</h2>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230702145608644.png" alt=""><figcaption>image-20230702145608644</figcaption>
</figure>
<p>假设发送端为客户端，接收端为服务端，开始时候两端<code>TCP</code>进程都处于<code>CLOSED</code>状态。</p>
<ul>
<li>第一次握手：客户端发送<code>SYN(seq=x)</code>报文给客户端，进入<code>SYN_SENT</code>状态。</li>
<li>第二次握手：服务端收到客户端发来的<code>SYN</code>报文，回应一个<code>SYN(seq=y)</code>和<code>ACK(ack=x+1)</code>报文，进入<code>SYN_RECV</code>状态。</li>
<li>第三次握手：客户端收到服务端发来的<code>SYN</code>报文，回应一个<code>ACK(ack=y+1)</code>报文，进入<code>Established</code>状态。</li>
</ul>
<h2 id="tcp四次挥手">TCP四次挥手</h2>
<figure>
<img data-src="https://codeleader.oss-cn-beijing.aliyuncs.com/site/image-20230805160515305.png" alt=""><figcaption>image-20230805160515305</figcaption>
</figure>
<p>假设客户端首先发起的断开连接请求</p>
<p><strong>第一次挥手</strong>：客户端向服务端发送的数据完成后，向服务端发起释放连接报文，报文包含标志位FIN=1，序列号seq=u。此时客户端只能接收数据，不能向服务端发送数据。</p>
<p><strong>第二次挥手</strong>：服务端收到客户端的释放连接报文后，向客户端发送确认报文，包含标志位ACK=1，序列号seq=v，确认号ack=u+1。此时客户端到服务端的连接已经释放掉，客户端不能向服务端发送数据，服务端也不能向客户端发送数据。但服务端到客户端的单向连接还能正常传输数据。</p>
<p><strong>第三次挥手</strong>：服务端发送完数据后向客户端发出连接释放报文，报文包含标志位FIN=1，标志位ACK=1，序列号seq=w，确认号ack=u+1。</p>
<p><strong>第四次挥手</strong>：客户端收到服务端发送的释放连接请求，向服务端发送确认报文，包含标志位ACK=1，序列号seq=u+1，确认号ack=w+1。</p>
<h2 id="tcp如何保证传输的可靠性待补充">TCP如何保证传输的可靠性(待补充)</h2>
<h2 id="键入网址到网页显示期间发生了什么待补充">键入网址到网页显示，期间发生了什么？(待补充)</h2>
<h1 id="操作系统">操作系统</h1>
<h1 id="参考链接">参考链接</h1>
<ul>
<li><a target="_blank" rel="noopener" href="https://xiaolincoding.com/">小林coding</a></li>
<li><a target="_blank" rel="noopener" href="https://www.mianshi.online/">路人张的面试笔记</a></li>
<li><a target="_blank" rel="noopener" href="https://javaguide.cn/">JavaGuide</a></li>
<li>《Offer来了Java面试核心知识点精讲》王磊</li>
<li>《Java程序员面试笔试宝典》何昊</li>
<li>《剑指Java》尚硅谷</li>
<li>《MySQL从入门到精通》</li>
<li>《基于Docker的Redis入门与实战》</li>
<li>《计算机网络》谢希仁</li>
<li>《计算机操作系统》徐甲同</li>
<li>《ElasticSearch搜索引擎构建与入门实战》高印会</li>
</ul>

    </div>

    
    
    

    <footer class="post-footer">
          

<div class="post-copyright">
<ul>
  <li class="post-copyright-author">
      <strong>本文作者： </strong>别团等shy哥发育
  </li>
  <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="https://codeleader.gitee.io/hexo/2023/08/11/%E9%9D%A2%E8%AF%95%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86%E7%82%B9%E6%80%BB%E7%BB%93/" title="面试知识点总结(持续更新中)">https://codeleader.gitee.io/hexo/2023/08/11/面试核心知识点总结/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>

          <div class="post-tags">
              <a href="/hexo/tags/Java/" rel="tag"># Java</a>
              <a href="/hexo/tags/%E9%9D%A2%E7%BB%8F/" rel="tag"># 面经</a>
              <a href="/hexo/tags/MySQL/" rel="tag"># MySQL</a>
              <a href="/hexo/tags/Redis/" rel="tag"># Redis</a>
              <a href="/hexo/tags/RabbitMQ/" rel="tag"># RabbitMQ</a>
              <a href="/hexo/tags/JVM/" rel="tag"># JVM</a>
              <a href="/hexo/tags/SpringBoot/" rel="tag"># SpringBoot</a>
              <a href="/hexo/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" rel="tag"># 设计模式</a>
              <a href="/hexo/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" rel="tag"># 计算机网络</a>
              <a href="/hexo/tags/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/" rel="tag"># 操作系统</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/hexo/2023/08/11/%E5%BF%AB%E6%89%8B%E4%B8%80%E9%9D%A2/" rel="prev" title="快手一面">
                  <i class="fa fa-chevron-left"></i> 快手一面
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/hexo/2023/08/13/%E7%AE%80%E5%8E%86%E9%A1%B9%E7%9B%AE%E6%80%BB%E7%BB%93/" rel="next" title="简历项目总结(谷粒学院+商品汇电商+油田项目)">
                  简历项目总结(谷粒学院+商品汇电商+油田项目) <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 2022 – 
  <span itemprop="copyrightYear">2024</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">别团等shy哥发育</span>
</div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
    <span title="站点总字数">81k</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
    <span title="站点阅读时长">4:56</span>
  </span>
</div>
<div class="busuanzi-count">
    <span class="post-meta-item" id="busuanzi_container_site_uv">
      <span class="post-meta-item-icon">
        <i class="fa fa-user"></i>
      </span>
      <span class="site-uv" title="总访客量">
        <span id="busuanzi_value_site_uv"></span>
      </span>
    </span>
    <span class="post-meta-item" id="busuanzi_container_site_pv">
      <span class="post-meta-item-icon">
        <i class="fa fa-eye"></i>
      </span>
      <span class="site-pv" title="总访问量">
        <span id="busuanzi_value_site_pv"></span>
      </span>
    </span>
</div>

<!-- <br /> -->
<!-- 网站运行时间的设置 -->
<span id="timeDate">载入天数...</span>
<!-- <span id="times">载入时分秒...</span> -->
<script>
    var now = new Date();
    function createtime() {
        var grt= new Date("11/17/2022 8:00:00");//此处修改你的建站时间或者网站上线时间
        now.setTime(now.getTime()+250);
        days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days);
        hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours);
        if(String(hnum).length ==1 ){hnum = "0" + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum);
        mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = "0" + mnum;}
        seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
        snum = Math.round(seconds); 
        if(String(snum).length ==1 ){snum = "0" + snum;}
        // var times = document.getElementById("times").innerHTML = hnum + " 小时 " + mnum + " 分 " + snum + " 秒";
        document.getElementById("timeDate").innerHTML = "本站已安全运行 "+dnum+" 天 "+hnum + " 小时 " + mnum + " 分 " + snum + " 秒";
    }
setInterval("createtime()",250);
</script>

    </div>
  </footer>

  

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/hexo/js/comments.js"></script><script src="/hexo/js/utils.js"></script><script src="/hexo/js/motion.js"></script><script src="/hexo/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/hexo/js/third-party/search/local-search.js"></script>




  <script src="/hexo/js/third-party/pace.js"></script>

  
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/hexo/js/third-party/math/mathjax.js"></script>



</body>
</html>
